/******************************************************************************* * Copyright (c) 2006, 2016 Intel Corporation and others. * 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: * Intel Corporation - Initial API and implementation *******************************************************************************/ package org.eclipse.cdt.managedbuilder.internal.buildmodel; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.cdt.core.cdtvariables.CdtVariableException; import org.eclipse.cdt.managedbuilder.buildmodel.BuildDescriptionManager; import org.eclipse.cdt.managedbuilder.buildmodel.IBuildCommand; import org.eclipse.cdt.managedbuilder.buildmodel.IBuildDescription; import org.eclipse.cdt.managedbuilder.buildmodel.IBuildIOType; import org.eclipse.cdt.managedbuilder.buildmodel.IBuildResource; import org.eclipse.cdt.managedbuilder.buildmodel.IBuildStep; import org.eclipse.cdt.managedbuilder.core.BuildException; import org.eclipse.cdt.managedbuilder.core.IBuildObject; import org.eclipse.cdt.managedbuilder.core.IBuilder; import org.eclipse.cdt.managedbuilder.core.IConfiguration; import org.eclipse.cdt.managedbuilder.core.IInputType; import org.eclipse.cdt.managedbuilder.core.IManagedCommandLineGenerator; import org.eclipse.cdt.managedbuilder.core.IManagedCommandLineInfo; import org.eclipse.cdt.managedbuilder.core.IOption; import org.eclipse.cdt.managedbuilder.core.ITool; import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; import org.eclipse.cdt.managedbuilder.internal.core.Configuration; import org.eclipse.cdt.managedbuilder.internal.core.Tool; import org.eclipse.cdt.managedbuilder.internal.macros.BuildMacroProvider; import org.eclipse.cdt.managedbuilder.internal.macros.FileContextData; import org.eclipse.cdt.managedbuilder.internal.macros.IMacroContextInfo; import org.eclipse.cdt.managedbuilder.macros.IBuildMacroProvider; import org.eclipse.cdt.managedbuilder.macros.IFileContextData; import org.eclipse.cdt.utils.cdtvariables.CdtVariableResolver; import org.eclipse.cdt.utils.cdtvariables.SupplierBasedCdtVariableSubstitutor; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; public class BuildStep implements IBuildStep { /** * When an argument of a command is joined into a String for preparation for running with exec, the * argument may need surrounding with quotes and have spaces between each argument. This padding allows * for that when constructing long commands. */ private static final int PER_ARGUMENT_PADDING = 3; /** * On Windows XP and above, the maximum command line length is 8191, on Linux it is at least 131072, but * that includes the environment. We want to limit the invocation of a single command to this number of * characters, and we want to ensure that the number isn't so low as to slow down operation. * * Doing each rm in its own command would be very slow, especially on Windows. */ private static final int MAX_CLEAN_LENGTH = 6000; private List<BuildIOType> fInputTypes = new ArrayList<BuildIOType>(); private List<BuildIOType> fOutputTypes = new ArrayList<BuildIOType>(); private ITool fTool; private BuildGroup fBuildGroup; private boolean fNeedsRebuild; private boolean fIsRemoved; private BuildDescription fBuildDescription; private IInputType fInputType; private ITool fLibTool; private boolean fAssignToCalculated; protected BuildStep(BuildDescription des, ITool tool, IInputType inputType) { fTool = tool; fInputType = inputType; fBuildDescription = des; if (DbgUtil.DEBUG) DbgUtil.trace("step " + DbgUtil.stepName(this) + " created"); //$NON-NLS-1$ //$NON-NLS-2$ des.stepCreated(this); } @Override public IBuildIOType[] getInputIOTypes() { return fInputTypes.toArray(new BuildIOType[fInputTypes.size()]); } @Override public IBuildIOType[] getOutputIOTypes() { return fOutputTypes.toArray(new BuildIOType[fOutputTypes.size()]); } @Override public boolean needsRebuild() { if (fNeedsRebuild || (fTool != null && fTool.needsRebuild()) || (fLibTool != null && fLibTool.needsRebuild())) return true; if (fBuildGroup != null && fBuildGroup.needsRebuild()) return true; return false; } public void setRebuildState(boolean rebuild) { fNeedsRebuild = rebuild; } public BuildResource[] removeIOType(BuildIOType type) { BuildResource rcs[] = type.remove(); if (type.isInput()) fInputTypes.remove(type); else fOutputTypes.remove(type); return rcs; } BuildResource[][] remove() { BuildResource[][] rcs = clear(); if (DbgUtil.DEBUG) DbgUtil.trace("step " + DbgUtil.stepName(this) + " removed"); //$NON-NLS-1$ //$NON-NLS-2$ fBuildDescription.stepRemoved(this); fBuildDescription = null; return rcs; } BuildResource[][] clear() { BuildResource[][] rcs = new BuildResource[2][]; rcs[0] = (BuildResource[]) getInputResources(); rcs[1] = (BuildResource[]) getOutputResources(); BuildIOType types[] = (BuildIOType[]) getInputIOTypes(); for (int i = 0; i < types.length; i++) { removeIOType(types[i]); } types = (BuildIOType[]) getOutputIOTypes(); for (int i = 0; i < types.length; i++) { removeIOType(types[i]); } return rcs; } public void removeResource(BuildIOType type, BuildResource rc, boolean rmTypeIfEmpty) { type.removeResource(rc); if (rmTypeIfEmpty && type.getResources().length == 0) { removeIOType(type); } } public BuildIOType createIOType(boolean input, boolean primary, /* String ext, */ IBuildObject ioType) { if (input) { if (fBuildDescription.getInputStep() == this) throw new IllegalArgumentException("input step can not have inputs"); //$NON-NLS-1$ } else { if (fBuildDescription.getOutputStep() == this) throw new IllegalArgumentException("input step can not have outputs"); //$NON-NLS-1$ } BuildIOType arg = new BuildIOType(this, input, primary, /* ext, */ ioType); if (input) fInputTypes.add(arg); else fOutputTypes.add(arg); return arg; } public void setTool(ITool tool) { fTool = tool; } public ITool getTool() { return fTool; } public BuildIOType[] getPrimaryTypes(boolean input) { List<BuildIOType> types = input ? fInputTypes : fOutputTypes; List<BuildIOType> list = new ArrayList<BuildIOType>(); for (BuildIOType arg : types) { if (arg.isPrimary()) list.add(arg); } return list.toArray(new BuildIOType[list.size()]); } public BuildIOType getIOTypeForType(IBuildObject ioType, boolean input) { List<BuildIOType> list = input ? fInputTypes : fOutputTypes; if (ioType != null) { for (BuildIOType arg : list) { if (arg.getIoType() == ioType) return arg; } } else { if (list.size() > 0) return list.get(0); } return null; } protected void setGroup(BuildGroup group) { fBuildGroup = group; } @Override public IBuildResource[] getInputResources() { return getResources(true); } @Override public IBuildResource[] getOutputResources() { return getResources(false); } public IBuildResource[] getResources(boolean input) { List<BuildIOType> list = input ? fInputTypes : fOutputTypes; Set<IBuildResource> set = new HashSet<IBuildResource>(); for (BuildIOType arg : list) { IBuildResource rcs[] = arg.getResources(); for (int j = 0; j < rcs.length; j++) { set.add(rcs[j]); } } return set.toArray(new BuildResource[set.size()]); } @Override public IBuildCommand[] getCommands(IPath cwd, Map inputArgValues, Map outputArgValues, boolean resolveAll) { if (cwd == null) cwd = calcCWD(); if (fTool == null) { if (this == fBuildDescription.getCleanStep()) { String cleanCmd = fBuildDescription.getConfiguration().getCleanCommand(); if (cleanCmd != null && (cleanCmd = cleanCmd.trim()).length() > 0) { List<IBuildCommand> list = new ArrayList<IBuildCommand>(); cleanCmd = resolveMacros(cleanCmd, resolveAll); String commands[] = cleanCmd.split(";"); //$NON-NLS-1$ for (int i = 0; i < commands.length - 1; i++) { list.add(createCommandFromString(commands[0], cwd, getEnvironment())); } List<String> cleanCmdArgs = convertStringToArguments(commands[commands.length - 1]); final int initialLen = cleanCmdArgs.stream() .mapToInt(w -> w.length() + PER_ARGUMENT_PADDING).sum(); IPath cleanCmdPath = new Path(cleanCmdArgs.get(0)); Map<String, String> env = getEnvironment(); IBuildResource[] resources = fBuildDescription.getResources(true); List<String> args = new ArrayList<>(); args.addAll(cleanCmdArgs.subList(1, cleanCmdArgs.size())); int totalLen = initialLen; for (IBuildResource resource : resources) { IPath resLoc = BuildDescriptionManager.getRelPath(cwd, resource.getLocation()); String path = resLoc.toString(); int pathLen = path.length() + PER_ARGUMENT_PADDING; if (totalLen + pathLen > MAX_CLEAN_LENGTH && totalLen != initialLen) { // adding new path takes us over limit, emit what we have... BuildCommand buildCommand = new BuildCommand(cleanCmdPath, args.toArray(new String[args.size()]), env, cwd, this); list.add(buildCommand); // ...and restart totalLen = initialLen; args.clear(); args.addAll(cleanCmdArgs.subList(1, cleanCmdArgs.size())); } args.add(path); totalLen += pathLen; } // add remaining files BuildCommand buildCommand = new BuildCommand(cleanCmdPath, args.toArray(new String[args.size()]), env, cwd, this); list.add(buildCommand); return list.toArray(new BuildCommand[list.size()]); } } else { String step = null; if (this == fBuildDescription.getInputStep()) { step = fBuildDescription.getConfiguration().getPrebuildStep(); } else if (this == fBuildDescription.getOutputStep()) { step = fBuildDescription.getConfiguration().getPostbuildStep(); } if (step != null && (step = step.trim()).length() > 0) { step = resolveMacros(step, resolveAll); if (step != null && (step = step.trim()).length() > 0) { String commands[] = step.split(";"); //$NON-NLS-1$ List<IBuildCommand> list = new ArrayList<IBuildCommand>(); for (int i = 0; i < commands.length; i++) { IBuildCommand cmds[] = createCommandsFromString(commands[i], cwd, getEnvironment()); for (int j = 0; j < cmds.length; j++) { list.add(cmds[j]); } } return list.toArray(new BuildCommand[list.size()]); } } } return new IBuildCommand[0]; } performAsignToOption(cwd); BuildResource inRc = getRcForMacros(true); BuildResource outRc = getRcForMacros(false); IPath inRcPath = inRc != null ? BuildDescriptionManager.getRelPath(cwd, inRc.getLocation()) : null; IPath outRcPath = outRc != null ? BuildDescriptionManager.getRelPath(cwd, outRc.getLocation()) : null; IManagedCommandLineGenerator gen = fTool.getCommandLineGenerator(); FileContextData data = new FileContextData(inRcPath, outRcPath, null, fTool); String outPrefix = fTool.getOutputPrefix(); outPrefix = resolveMacros(outPrefix, data, true); outRcPath = rmNamePrefix(outRcPath, outPrefix); IManagedCommandLineInfo info = gen.generateCommandLineInfo(fTool, fTool.getToolCommand(), getCommandFlags(inRcPath, outRcPath, resolveAll), fTool.getOutputFlag(), outPrefix, listToString(resourcesToStrings(cwd, getPrimaryResources(false), outPrefix), " "), //$NON-NLS-1$ getInputResources(cwd, getPrimaryResources(true)), fTool.getCommandLinePattern()); return createCommandsFromString(resolveMacros(info.getCommandLine(), data, true), cwd, getEnvironment()); } private IPath rmNamePrefix(IPath path, String prefix) { if (prefix != null && prefix.length() != 0) { String name = path.lastSegment(); if (name.startsWith(prefix)) { name = name.substring(prefix.length()); path = path.removeLastSegments(1).append(name); } } return path; } private String[] getInputResources(IPath cwd, BuildResource[] rcs) { String[] resources = resourcesToStrings(cwd, rcs, null); // also need to get libraries String[] libs = null; IOption[] opts = fTool.getOptions(); for (int i = 0; i < opts.length; ++i) { try { IOption opt = opts[i]; if (opt.getValueType() == IOption.LIBRARIES) { String[] l = opts[i].getLibraries(); if (l != null) { libs = new String[l.length]; for (int j = 0; j < l.length; ++j) { libs[j] = opt.getCommand() + l[j]; } } } } catch (BuildException e) { } } if (libs != null) { String[] irs = new String[resources.length + libs.length]; System.arraycopy(resources, 0, irs, 0, resources.length); System.arraycopy(libs, 0, irs, resources.length, libs.length); return irs; } else { return resources; } } private IPath calcCWD() { IPath cwd = fBuildDescription.getDefaultBuildDirLocation(); if (!cwd.isAbsolute()) cwd = fBuildDescription.getConfiguration().getOwner().getProject().getLocation().append(cwd); return cwd; } protected Map<String, String> getEnvironment() { return fBuildDescription.getEnvironment(); } protected IBuildCommand[] createCommandsFromString(String cmd, IPath cwd, Map<String, String> env) { IBuildCommand buildCommand = createCommandFromString(cmd, cwd, env); return new IBuildCommand[] { buildCommand }; } protected IBuildCommand createCommandFromString(String cmd, IPath cwd, Map<String, String> env) { List<String> list = convertStringToArguments(cmd); IPath c = new Path(list.remove(0)); String[] args = list.toArray(new String[list.size()]); BuildCommand buildCommand = new BuildCommand(c, args, env, cwd, this); return buildCommand; } /** * Convert string to arguments, first argument is command * * @param commandLine * to parse * @return arguments as a list */ protected List<String> convertStringToArguments(String commandLine) { char arr[] = commandLine.toCharArray(); char expect = 0; char prev = 0; // int start = 0; List<String> list = new ArrayList<String>(); StringBuilder buf = new StringBuilder(); for (int i = 0; i < arr.length; i++) { char ch = arr[i]; switch (ch) { case '\'': case '"': if (expect == ch) { // String s = cmd.substring(start, i); // list.add(s); expect = 0; // start = i + 1; } else if (expect == 0) { // String s = cmd.substring(start, i); // list.add(s); expect = ch; // start = i + 1; } else { buf.append(ch); } break; case ' ': if (expect == 0) { if (prev != ' ') { list.add(buf.toString()); buf.delete(0, buf.length()); } // start = i + 1; } else { buf.append(ch); } break; default: buf.append(ch); break; } prev = ch; } if (buf.length() > 0) list.add(buf.toString()); return list; } private BuildResource[] getPrimaryResources(boolean input) { BuildIOType[] types = getPrimaryTypes(input); if (types.length == 0) types = input ? (BuildIOType[]) getInputIOTypes() : (BuildIOType[]) getOutputIOTypes(); List<BuildResource> list = new ArrayList<BuildResource>(); for (int i = 0; i < types.length; i++) { BuildResource[] rcs = (BuildResource[]) types[i].getResources(); for (int j = 0; j < rcs.length; j++) { list.add(rcs[j]); } } return list.toArray(new BuildResource[list.size()]); } private String[] resourcesToStrings(IPath cwd, BuildResource rcs[], String prefixToRm) { List<String> list = new ArrayList<String>(rcs.length); for (int i = 0; i < rcs.length; i++) { IPath path = BuildDescriptionManager.getRelPath(cwd, rcs[i].getLocation()); path = rmNamePrefix(path, prefixToRm); list.add(path.toOSString()); } return list.toArray(new String[list.size()]); } private String resolveMacros(String str, IFileContextData fileData, boolean resolveAll) { String result = str; try { if (resolveAll) { IConfiguration cfg = getBuildDescription().getConfiguration(); IBuilder builder = cfg.getBuilder(); SupplierBasedCdtVariableSubstitutor sub = createSubstitutor(cfg, builder, fileData); result = CdtVariableResolver.resolveToString(str, sub); } else { result = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(str, "", //$NON-NLS-1$ " ", IBuildMacroProvider.CONTEXT_FILE, fileData); //$NON-NLS-1$ } } catch (CdtVariableException e) { } return result; } private String resolveMacros(String str, boolean resolveAll) { String result = str; try { IConfiguration cfg = getBuildDescription().getConfiguration(); if (resolveAll) { result = ManagedBuildManager.getBuildMacroProvider().resolveValue(str, "", " ", //$NON-NLS-1$ //$NON-NLS-2$ IBuildMacroProvider.CONTEXT_CONFIGURATION, cfg); } else { result = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(str, "", //$NON-NLS-1$ " ", IBuildMacroProvider.CONTEXT_CONFIGURATION, cfg); //$NON-NLS-1$ } } catch (CdtVariableException e) { } return result; } private SupplierBasedCdtVariableSubstitutor createSubstitutor(IConfiguration cfg, IBuilder builder, IFileContextData fileData) { BuildMacroProvider prov = (BuildMacroProvider) ManagedBuildManager.getBuildMacroProvider(); IMacroContextInfo info = prov.getMacroContextInfo(IBuildMacroProvider.CONTEXT_FILE, fileData); FileMacroExplicitSubstitutor sub = new FileMacroExplicitSubstitutor(info, cfg, builder, "", " "); //$NON-NLS-1$ //$NON-NLS-2$ return sub; } private String[] getCommandFlags(IPath inRcPath, IPath outRcPath, boolean resolveAll) { try { if (resolveAll) { IConfiguration cfg = getBuildDescription().getConfiguration(); IBuilder builder = cfg.getBuilder(); return ((Tool) fTool).getToolCommandFlags(inRcPath, outRcPath, createSubstitutor(cfg, builder, new FileContextData(inRcPath, outRcPath, null, fTool)), BuildMacroProvider.getDefault()); } return fTool.getToolCommandFlags(inRcPath, outRcPath); } catch (BuildException e) { } return new String[0]; } private String listToString(String[] list, String delimiter) { if (list == null || list.length == 0) return ""; //$NON-NLS-1$ StringBuilder buf = new StringBuilder(list[0]); for (int i = 1; i < list.length; i++) { buf.append(delimiter).append(list[i]); } return buf.toString(); } private BuildResource getRcForMacros(boolean input) { IBuildIOType types[] = getPrimaryTypes(input); if (types.length != 0) { for (int i = 0; i < types.length; i++) { IBuildResource rcs[] = types[i].getResources(); if (rcs.length != 0) return (BuildResource) rcs[0]; } } types = input ? getInputIOTypes() : getOutputIOTypes(); if (types.length != 0) { for (int i = 0; i < types.length; i++) { IBuildResource rcs[] = types[i].getResources(); if (rcs.length != 0) return (BuildResource) rcs[0]; } } return null; } @Override public boolean isRemoved() { return fIsRemoved; } public void setRemoved() { fIsRemoved = true; fNeedsRebuild = false; } @Override public IBuildDescription getBuildDescription() { return fBuildDescription; } boolean isMultiAction() { BuildIOType args[] = getPrimaryTypes(true); BuildIOType arg = args.length > 0 ? args[0] : null; if (arg != null) { if (arg.getIoType() != null) return ((IInputType) arg.getIoType()).getMultipleOfType(); return fTool != null && fTool == ((Configuration) fBuildDescription.getConfiguration()).calculateTargetTool(); } return false; } public IInputType getInputType() { return fInputType; } public void setLibTool(ITool libTool) { fLibTool = libTool; } public ITool getLibTool() { return fLibTool; } protected void performAsignToOption(IPath cwd) { if (fTool == null && !fAssignToCalculated) return; fAssignToCalculated = true; IConfiguration cfg = fBuildDescription.getConfiguration(); for (BuildIOType bType : fInputTypes) { IInputType type = (IInputType) bType.getIoType(); if (type == null) continue; IOption option = fTool.getOptionBySuperClassId(type.getOptionId()); IOption assignToOption = fTool.getOptionBySuperClassId(type.getAssignToOptionId()); if (assignToOption != null && option == null) { try { BuildResource bRcs[] = (BuildResource[]) bType.getResources(); int optType = assignToOption.getValueType(); if (optType == IOption.STRING) { String optVal = ""; //$NON-NLS-1$ for (int j = 0; j < bRcs.length; j++) { if (j != 0) { optVal += " "; //$NON-NLS-1$ } optVal += BuildDescriptionManager.getRelPath(cwd, bRcs[j].getLocation()) .toOSString(); } ManagedBuildManager.setOption(cfg, fTool, assignToOption, optVal); } else if (optType == IOption.STRING_LIST || optType == IOption.LIBRARIES || optType == IOption.OBJECTS || optType == IOption.INCLUDE_PATH || optType == IOption.PREPROCESSOR_SYMBOLS || optType == IOption.INCLUDE_FILES || optType == IOption.LIBRARY_PATHS || optType == IOption.LIBRARY_FILES || optType == IOption.MACRO_FILES || optType == IOption.UNDEF_INCLUDE_PATH || optType == IOption.UNDEF_PREPROCESSOR_SYMBOLS || optType == IOption.UNDEF_INCLUDE_FILES || optType == IOption.UNDEF_LIBRARY_PATHS || optType == IOption.UNDEF_LIBRARY_FILES || optType == IOption.UNDEF_MACRO_FILES) { // Mote that when using the enumerated inputs, the path(s) must be translated from // project relative // to top build directory relative String[] paths = new String[bRcs.length]; for (int j = 0; j < bRcs.length; j++) { paths[j] = BuildDescriptionManager.getRelPath(cwd, bRcs[j].getLocation()) .toOSString(); } ManagedBuildManager.setOption(cfg, fTool, assignToOption, paths); } else if (optType == IOption.BOOLEAN) { if (bRcs.length > 0) { ManagedBuildManager.setOption(cfg, fTool, assignToOption, true); } else { ManagedBuildManager.setOption(cfg, fTool, assignToOption, false); } } else if (optType == IOption.ENUMERATED || optType == IOption.TREE) { if (bRcs.length > 0) { ManagedBuildManager.setOption(cfg, fTool, assignToOption, BuildDescriptionManager .getRelPath(cwd, bRcs[0].getLocation()).toOSString()); } } } catch (BuildException ex) { } } } } }