/******************************************************************************* * Copyright (c) 2005, 2014 Synopsys, Incorporated * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Synopsys, Inc. - Initial implementation * Synopsys, Inc. - ARC GNU Toolchain support *******************************************************************************/ package com.arc.cdt.toolchain; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.cdt.managedbuilder.core.BuildException; import org.eclipse.cdt.managedbuilder.core.IBuildObject; import org.eclipse.cdt.managedbuilder.core.IConfiguration; import org.eclipse.cdt.managedbuilder.core.IHoldsOptions; import org.eclipse.cdt.managedbuilder.core.IOption; import org.eclipse.cdt.managedbuilder.core.IResourceInfo; import org.eclipse.cdt.managedbuilder.core.ITool; import org.eclipse.cdt.managedbuilder.core.IToolChain; import org.eclipse.cdt.managedbuilder.internal.core.ISettingsChangeListener; import org.eclipse.cdt.managedbuilder.internal.core.NotificationManager; import org.eclipse.cdt.managedbuilder.internal.core.SettingsChangeEvent; @SuppressWarnings("restriction") public abstract class AbstractOptionEnablementManager implements IOptionEnablementManager, ISettingsChangeListener { private Map<String, Object> mValueMap = new HashMap<String, Object>(); private Set<String> mDisabledSet = new HashSet<String>(); private List<IObserver> mObservers = new ArrayList<IObserver>(); private IResourceInfo mConfig; private IToolChain mLastToolChain; protected transient boolean initializing = false; private boolean clangBased = false; private ITool[] mTools; private Map<String, Object[]> toolChainOptionValues = new HashMap<>(); public AbstractOptionEnablementManager() { NotificationManager.getInstance().subscribe(this); } protected IToolChain getToolChain() { return mLastToolChain; } protected IResourceInfo getConfig() { return mConfig; } protected Object[] getOptionAndValueFromCommand(String command) { return toolChainOptionValues.get(command); } @Override public void initialize (IBuildObject config) { //cr92699: when the toolchain is changed, CDT still delivers the original configuration! // Thus, we are not aware that anything needs to be recomputed. // To get around this, we track a change in the toolchain. IToolChain toolchain = null; if (config instanceof IConfiguration){ toolchain =((IConfiguration)config).getToolChain(); config = ((IConfiguration)config).getRootFolderInfo(); } else if (config instanceof IResourceInfo){ IConfiguration cfg =((IResourceInfo)config).getParent(); if (cfg != null) toolchain = cfg.getToolChain(); } //System.out.println("toolchain.OptionEnableManager~~~~~~~~~~~~~~~~~~~~~~~~~~~"+toolchain.getBaseId()); if ((config != mConfig || toolchain != mLastToolChain) && config instanceof IResourceInfo) { initializing = true; try { mDisabledSet.clear(); mValueMap.clear(); mConfig = (IResourceInfo)config; mLastToolChain = toolchain; ITool tools[] = mConfig.getTools(); mTools = tools; for (IOption option : toolchain.getOptions()) { /* * Get all applicable values for the option and put them into a map. For each * applicable value get flag which is passed to compiler if this value is * selected and use it as a key. This then will be used to synchronize flags * from TCF with option values in IDE in case TCF is chosen. */ String[] appValues = option.getApplicableValues(); if (appValues != null && appValues.length > 0) { for (String appValue: appValues) { String key = null; try { key = option.getEnumCommand(appValue); } catch (BuildException e) { e.printStackTrace(); } if (key != null) { try { toolChainOptionValues.put(key, new Object[]{option, option.getEnumeratedId(appValue)}); } catch (BuildException e) { e.printStackTrace(); } } } } else { String key = option.getCommand(); if (key != null) { toolChainOptionValues.put(key, new Object[]{option, true}); if (key.startsWith("-m")) { String noKey = key.substring(0, 2) + "no-" + key.substring(2); toolChainOptionValues.put(noKey, new Object[]{option, false}); } } } } /* First read applicable values for all the options, then start to set options * so that when we set processor value, we could also set all the other necessary * values to other options. */ for (IOption option : toolchain.getOptions()) { set(option.getBaseId(), option.getValue()); } for (ITool tool : tools) { for (IOption option : tool.getOptions()) { set(option.getBaseId(), option.getValue()); } } } finally { initializing = false; } } } protected boolean isClangBased() {return clangBased; } protected void setClangBased(boolean v) { clangBased = v; } /** * Return holder and option. * <P> * NOTE: option.getHolder() is not necessarily accurate! * @param id * @return holder and option or null. */ protected Object[] getOption(String id){ // We cannot cannot create an ID-to-Option map because the IOption object // changes when it becomes dirty! if (mLastToolChain == null) { return null; } for (IOption option : mLastToolChain.getOptions()) { if (id.equals(option.getBaseId())){ return new Object[]{mLastToolChain, option}; } } if (mTools == null) return null; for (ITool tool : mTools) { for (IOption option : tool.getOptions()) { if (id.equals(option.getBaseId())){ return new Object[]{tool,option}; } // These codes are customized for ARC GNU toolchain else { Object oParent = tool.getParent(); while ((oParent != null) && (!(oParent instanceof IToolChain))) { Object oSuper = tool.getSuperClass(); if ((oSuper != null) && ((oSuper instanceof ITool))) oParent = ((ITool)oSuper).getParent(); else { oParent = null; } } if ((oParent != null) && ((oParent instanceof IToolChain))) { IToolChain oToolChain = (IToolChain)oParent; IOption[] aoOptions = oToolChain.getOptions(); for (int i = 0; i < aoOptions.length; i++) { IOption oOption = aoOptions[i]; String sID = oOption.getId(); if (sID.contains(id)){ return new Object[]{tool,oOption}; //change option to oOption, can get the latest processor value } } } } } } return null; } /** * Returns corresponding to this option compiler flag. If there are several possible values, * returns one of them. * * @param optionId * to get command from * @return command corresponding to the option. If option has several enumerated values, get * command from one of the values. */ protected String getCommand(String optionId) { IOption option = (IOption)getOption(optionId)[1]; String[] applicableValues = option.getApplicableValues(); if (applicableValues != null) { for (String appValue: applicableValues) { try { String value = option.getEnumCommand(appValue); if (!value.isEmpty()) { return value; } } catch (BuildException e) { e.printStackTrace(); } } } return option.getCommand(); } // Made public so that various subclasses can access this method from each other. public void setOptionValue (String id, Object value) { // If this is called as side-effect of initializing, we can get // NPE and stuff. if (initializing) return; Object[] target = getOption(id); if (target == null) { // Don't complain about unknown ID. If someone dynamically changed // the project type, there may be old references that no longer apply. //throw new IllegalArgumentException("Unknown option id: " + id); return; } //NOTE: opt.getHolder() is not necessarily accurate!! //IHoldsOptions h = (IHoldsOptions)target[0]; IOption opt = (IOption)target[1]; try { if (value instanceof String) { //mConfig.setOption(h, opt, (String) value); opt.setValue((String) value); } else if (value instanceof Boolean) { //mConfig.setOption(h, opt, ((Boolean) value).booleanValue()); opt.setValue(((Boolean) value).booleanValue()); } else throw new IllegalArgumentException("Invalid value to set option " + id + ": " + value); } catch (BuildException e) { throw new IllegalArgumentException("Can't set value for " + id, e); } } public IOption setOption(IHoldsOptions parent, IOption option, boolean value) throws BuildException { // Is there a change? IOption retOpt = option; boolean oldVal = option.getBooleanValue(); if (oldVal != value) { //retOpt = parent.getOptionToSet(option, false); retOpt.setValue(value); //NotificationManager.getInstance().optionChanged(this, parent, option, new Boolean(oldVal)); } return retOpt; } /** * Return the set of all options. * @return the set of all options. */ protected Set<String> getOptionIds () { return mValueMap.keySet(); } /** * This is expected to be overridden in subclasses, but to be called from the overriding method. * @param optionId the id of the option. * @param value the new value of the option. */ @Override public void set (String optionId, Object value) { Object prev = mValueMap.put(optionId, value); if (prev == null || !prev.equals(value)) { fireValueChange(optionId); } } @Override public Object getValue (String optionId) { return mValueMap.get(optionId); } @Override public boolean isEnabled (String optionId) { return !mDisabledSet.contains(optionId); } public boolean disenabled (String optionId) { return mDisabledSet.contains(optionId); } // public so as to be called by other OptionEnablementManagers public void setEnabled (String optionId, boolean v) { if (optionId == null) { return; } if (isEnabled(optionId) != v) { if (v) mDisabledSet.remove(optionId); else mDisabledSet.add(optionId); fireEnablementChange(optionId); } } public void setEnabled (List<String> optionIds, boolean v) { for (String optionId : optionIds) { setEnabled(optionId, v); } } @Override public void addObserver (IObserver observer) { synchronized (mObservers) { mObservers.add(observer); } } @Override public void removeObserver (IObserver observer) { synchronized (mObservers) { mObservers.remove(observer); } } /** * Notify observers that the value of an option changed. * @param id the id of the option. */ private void fireValueChange (String id) { fireChange(id, true); } /** * Notify observers that the enble property of an option changed. * @param id the id of the option. */ private void fireEnablementChange (String id) { fireChange(id, false); } private int mNestedChangeLevel = 0; private void fireChange (String id, boolean valueChanged) { IObserver[] observers; synchronized (mObservers) { int cnt = mObservers.size(); if (cnt == 0) return; observers = mObservers.toArray(new IObserver[cnt]); } mNestedChangeLevel++; try { //TN: mNestedChangeLevel increased from 10 to 20 because we have many level hierachical auto settings // which will increase this value faster than it can be released. Ex: mpy hierachical option settings for ARCV2HS) if (mNestedChangeLevel > 20) { throw new IllegalStateException("change observers are looping!"); } for (IObserver o : observers) { if (valueChanged) o.onOptionValueChanged(this, id); else o.onOptionEnablementChanged(this, id); } } finally { mNestedChangeLevel--; } } @Override public void settingsChanged(SettingsChangeEvent event) { IOption option = event.getOption(); // Due to CDT bug option is the original option such that option.getValue() does not // necessarily reflect the new value. We must retrieve the modified instance of option // These codes are customized for ARC GNU toolchain Object[] optLookup = getOption(option.getBaseId()); if(optLookup != null) option = (IOption) optLookup[1]; set(option.getBaseId(),option.getValue()); } }