com.github.maven_nar.cpptasks.CCTask Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nar-maven-plugin Show documentation
Show all versions of nar-maven-plugin Show documentation
This plugin compiles native code and publishes native artifacts in the form of nar files.
/*
* #%L
* Native ARchive plugin for Maven
* %%
* Copyright (C) 2002 - 2014 NAR Maven Plugin developers.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package com.github.maven_nar.cpptasks;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Environment;
import org.apache.commons.io.FilenameUtils;
import com.github.maven_nar.cpptasks.compiler.CompilerConfiguration;
import com.github.maven_nar.cpptasks.compiler.LinkType;
import com.github.maven_nar.cpptasks.compiler.Linker;
import com.github.maven_nar.cpptasks.compiler.LinkerConfiguration;
import com.github.maven_nar.cpptasks.compiler.Processor;
import com.github.maven_nar.cpptasks.compiler.ProcessorConfiguration;
import com.github.maven_nar.cpptasks.compiler.AbstractCompiler;
import com.github.maven_nar.cpptasks.compiler.CommandLineCompilerConfiguration;
import com.github.maven_nar.cpptasks.ide.ProjectDef;
import com.github.maven_nar.cpptasks.types.CompilerArgument;
import com.github.maven_nar.cpptasks.types.ConditionalFileSet;
import com.github.maven_nar.cpptasks.types.DefineSet;
import com.github.maven_nar.cpptasks.types.IncludePath;
import com.github.maven_nar.cpptasks.types.LibrarySet;
import com.github.maven_nar.cpptasks.types.LinkerArgument;
import com.github.maven_nar.cpptasks.types.SystemIncludePath;
import com.github.maven_nar.cpptasks.types.SystemLibrarySet;
/**
* Compile and link task.
*
*
* This task can compile various source languages and produce executables,
* shared libraries (aka DLL's) and static libraries. Compiler adaptors are
* currently available for several C/C++ compilers, FORTRAN, MIDL and Windows
* Resource files.
*
*
* @author Adam Murdoch
* @author Curt Arnold
*/
public class CCTask extends Task {
// BEGINFREEHEP
class Core extends Thread {
private final CCTask task;
private final CompilerConfiguration config;
private final File objDir;
private final List sourceFiles;
private final boolean relentless;
private final CCTaskProgressMonitor monitor;
private Exception compileException;
Core(final CCTask task, final int coreNo, final CompilerConfiguration config, final File objDir,
final List set, final boolean relentless, final CCTaskProgressMonitor monitor) {
super("Core " + coreNo);
this.task = task;
this.config = config;
this.objDir = objDir;
this.sourceFiles = set;
this.relentless = relentless;
this.monitor = monitor;
}
public Exception getException() {
return this.compileException;
}
@Override
public void run() {
super.run();
try {
String[] sources = new String[this.sourceFiles.size()];
sources = this.sourceFiles.toArray(sources);
this.config.compile(this.task, this.objDir, sources, this.relentless, this.monitor);
} catch (final Exception ex) {
if (this.compileException == null) {
this.compileException = ex;
}
}
}
}
// BEGINFREEHEP
class Progress extends Thread {
private boolean stop = false;
private final File objDir;
private final int rebuildCount;
public Progress(final File objDir, final int rebuildCount) {
this.objDir = objDir;
this.rebuildCount = rebuildCount;
}
public void exit() {
this.stop = true;
}
@Override
public void run() {
if (this.rebuildCount < 10) {
return;
}
try {
final FileFilter updatedFiles = new FileFilter() {
private final long startTime = System.currentTimeMillis();
@Override
public boolean accept(final File file) {
return file.lastModified() > this.startTime && !file.getName().endsWith(".xml");
}
};
while (!this.stop) {
System.err.print("\r" + this.objDir.listFiles(updatedFiles).length + " / " + this.rebuildCount
+ " files compiled...");
System.err.print("\r");
System.err.flush();
if (!this.stop) {
Thread.sleep(5000);
}
}
} catch (final InterruptedException e) {
}
System.err.print("\r ");
System.err.print("\r");
System.err.flush();
log(Integer.toString(this.rebuildCount) + " files were compiled.");
}
}
private static class ProjectFileCollector implements FileVisitor {
private final List files;
/**
* Creates a new ProjectFileCollector.
*
* @param files
* vector for collected files.
*/
public ProjectFileCollector(final List files) {
this.files = files;
}
/**
* Called for each file to be considered for collection.
*
* @param parentDir
* parent directory
* @param filename
* filename within directory
*/
@Override
public void visit(final File parentDir, final String filename) {
this.files.add(new File(parentDir, filename));
}
}
private static class SystemLibraryCollector implements FileVisitor {
private final Hashtable libraries;
private final Linker linker;
public SystemLibraryCollector(final Linker linker, final Hashtable libraries) {
this.linker = linker;
this.libraries = libraries;
}
@Override
public void visit(final File basedir, final String filename) {
if (this.linker.bid(filename) > 0) {
final File libfile = new File(basedir, filename);
final String key = this.linker.getLibraryKey(libfile);
this.libraries.put(key, libfile);
}
}
}
private static final ProcessorConfiguration[] EMPTY_CONFIG_ARRAY = new ProcessorConfiguration[0];
/**
* Builds a Hashtable to targets needing to be rebuilt keyed by compiler
* configuration
*/
public static Map> getTargetsToBuildByConfiguration(
final Map targets) {
final Map> targetsByConfig = new HashMap<>();
for (final TargetInfo target : targets.values()) {
if (target.getRebuild()) {
// FIXME: Types do not match between the key of targetsByConfig and the return value of target.getConfiguration
Vector targetsForSameConfig = targetsByConfig.get(target.getConfiguration());
if (targetsForSameConfig != null) {
targetsForSameConfig.addElement(target);
} else {
targetsForSameConfig = new Vector<>();
targetsForSameConfig.addElement(target);
targetsByConfig.put((CompilerConfiguration) target.getConfiguration(), targetsForSameConfig);
}
}
}
return targetsByConfig;
}
// FREEHEP
private int maxCores = 0;
private boolean ordered = false;
/** The compiler definitions. */
private final Vector _compilers = new Vector<>();
/** The output file type. */
// private LinkType _linkType = LinkType.EXECUTABLE;
/** The library sets. */
private final Vector _libsets = new Vector();
/** The linker definitions. */
private final Vector _linkers = new Vector<>();
/** The object directory. */
private File _objDir;
/** The output file. */
private File _outfile;
/** The linker definitions. */
private final Vector targetPlatforms = new Vector<>();
/** The distributer definitions. */
private final Vector distributers = new Vector<>();
private final Vector versionInfos = new Vector<>();
private final Vector projects = new Vector<>();
private boolean projectsOnly = false;
private boolean decorateLinkerOptions = true;
/**
* If true, stop build on compile failure.
*/
protected boolean failOnError = true;
/**
* Content that appears in and also in are maintained by a
* captive CompilerDef instance
*/
private final CompilerDef compilerDef = new CompilerDef();
/** The OS390 dataset to build to object to */
private String dataset;
/**
*
* Depth of dependency checking
*
* Values < 0 indicate full dependency checking Values >= 0 indicate
* partial dependency checking and for superficial compilation checks. Will
* throw BuildException before attempting link
*/
private int dependencyDepth = -1;
/**
* Content that appears in and also in are maintained by a
* captive CompilerDef instance
*/
private final LinkerDef linkerDef = new LinkerDef();
/**
* contains the subsystem, output type and
*
*/
private final LinkType linkType = new LinkType();
/**
* The property name which will be set with the physical filename of the
* file that is generated by the linker
*/
private String outputFileProperty;
/**
* if relentless = true, compilations should attempt to compile as many
* files as possible before throwing a BuildException
*/
private boolean relentless;
/**
* At which log-level do we log command-lines in the build?
*/
private int commandLogLevel = Project.MSG_VERBOSE;
/**
* If non-empty, compile an object of this name and package it
* into a shared archive with the specified output name (AIX only)
*/
private String sharedObjectName = "";
public CCTask() {
}
/**
* Adds a compiler definition or reference.
*
* @param compiler
* compiler
* @throws NullPointerException
* if compiler is null
*/
public void addConfiguredCompiler(final CompilerDef compiler) {
if (compiler == null) {
throw new NullPointerException("compiler");
}
compiler.setProject(getProject());
this._compilers.addElement(compiler);
}
/**
* Adds a compiler command-line arg. Argument will be inherited by all
* nested compiler elements that do not have inherit="false".
*
*/
public void addConfiguredCompilerArg(final CompilerArgument arg) {
this.compilerDef.addConfiguredCompilerArg(arg);
}
/**
* Adds a defineset. Will be inherited by all compiler elements that do not
* have inherit="false".
*
* @param defs
* Define set
*/
public void addConfiguredDefineset(final DefineSet defs) {
this.compilerDef.addConfiguredDefineset(defs);
}
/**
* Adds a distributer definition or reference (Non-functional prototype).
*
* @param distributer
* distributer
* @throws NullPointerException
* if compiler is null
*/
public void addConfiguredDistributer(final DistributerDef distributer) {
if (distributer == null) {
throw new NullPointerException("distributer");
}
distributer.setProject(getProject());
this.distributers.addElement(distributer);
}
/**
* Adds a linker definition. The first linker that is not disqualified by
* its "if" and "unless" attributes will perform the link. If no child
* linker element is active, the linker implied by the cc elements name or
* classname attribute will be used.
*
* @param linker
* linker
* @throws NullPointerException
* if linker is null
*/
public void addConfiguredLinker(final LinkerDef linker) {
if (linker == null) {
throw new NullPointerException("linker");
}
linker.setProject(getProject());
this._linkers.addElement(linker);
}
/**
* Adds a linker command-line arg. Argument will be inherited by all nested
* linker elements that do not have inherit="false".
*/
public void addConfiguredLinkerArg(final LinkerArgument arg) {
this.linkerDef.addConfiguredLinkerArg(arg);
}
/**
* Adds a target definition or reference (Non-functional prototype).
*
* @param target
* target
* @throws NullPointerException
* if compiler is null
*/
public void addConfiguredTarget(final TargetDef target) {
if (target == null) {
throw new NullPointerException("target");
}
target.setProject(getProject());
this.targetPlatforms.addElement(target);
}
/**
* Adds descriptive version information to be included in the
* generated file. The first active version info block will
* be used.
*/
public void addConfiguredVersioninfo(final VersionInfo newVersionInfo) {
newVersionInfo.setProject(this.getProject());
this.versionInfos.addElement(newVersionInfo);
}
/**
* Add an environment variable to the launched process.
*/
public void addEnv(final Environment.Variable var) {
this.compilerDef.addEnv(var);
for (int i = 0; i < this._compilers.size(); i++) {
final CompilerDef currentCompilerDef = this._compilers.elementAt(i);
currentCompilerDef.addEnv(var);
}
this.linkerDef.addEnv(var);
}
/**
* Adds a source file set.
*
* Files in these filesets will be auctioned to the available compiler
* configurations, with the default compiler implied by the cc element
* bidding last. If no compiler is interested in the file, it will be
* passed to the linker.
*
* To have a file be processed by a particular compiler configuration, add
* a fileset to the corresponding compiler element.
*/
public void addFileset(final ConditionalFileSet srcSet) {
this.compilerDef.addFileset(srcSet);
}
/**
* Adds a library set.
*
* Library sets will be inherited by all linker elements that do not have
* inherit="false".
*
* @param libset
* library set
* @throws NullPointerException
* if libset is null.
*/
public void addLibset(final LibrarySet libset) {
if (libset == null) {
throw new NullPointerException("libset");
}
this.linkerDef.addLibset(libset);
}
/**
* Specifies the generation of IDE project file. Experimental.
*
* @param projectDef
* project file generation specification
*/
public void addProject(final ProjectDef projectDef) {
if (projectDef == null) {
throw new NullPointerException("projectDef");
}
this.projects.addElement(projectDef);
}
/**
* Adds a system library set. Timestamps and locations of system library
* sets are not used in dependency analysis.
*
* Essential libraries (such as C Runtime libraries) should not be
* specified since the task will attempt to identify the correct libraries
* based on the multithread, debug and runtime attributes.
*
* System library sets will be inherited by all linker elements that do not
* have inherit="false".
*
* @param libset
* library set
* @throws NullPointerException
* if libset is null.
*/
public void addSyslibset(final SystemLibrarySet libset) {
if (libset == null) {
throw new NullPointerException("libset");
}
this.linkerDef.addSyslibset(libset);
}
/**
* Checks all targets that are not forced to be rebuilt or are missing
* object files to be checked for modified include files
*
* @return total number of targets to be rebuilt
*
*/
protected int checkForChangedIncludeFiles(final Map targets) {
int potentialTargets = 0;
int definiteTargets = 0;
Iterator targetEnum = targets.values().iterator();
while (targetEnum.hasNext()) {
final TargetInfo target = targetEnum.next();
if (!target.getRebuild()) {
potentialTargets++;
} else {
definiteTargets++;
}
}
//
// If there were remaining targets that
// might be out of date
//
if (potentialTargets > 0) {
log("Starting dependency analysis for " + Integer.toString(potentialTargets) + " files.");
final DependencyTable dependencyTable = new DependencyTable(this._objDir);
try {
dependencyTable.load();
} catch (final Exception ex) {
log("Problem reading dependencies.xml: " + ex.toString());
}
targetEnum = targets.values().iterator();
while (targetEnum.hasNext()) {
final TargetInfo target = targetEnum.next();
if (!target.getRebuild() && dependencyTable.needsRebuild(this, target, this.dependencyDepth)) {
target.mustRebuild();
}
}
dependencyTable.commit(this);
}
//
// count files being rebuilt now
//
int currentTargets = 0;
targetEnum = targets.values().iterator();
while (targetEnum.hasNext()) {
final TargetInfo target = targetEnum.next();
if (target.getRebuild()) {
currentTargets++;
}
}
if (potentialTargets > 0) {
log(Integer.toString(potentialTargets - currentTargets + definiteTargets) + " files are up to date.");
log(Integer.toString(currentTargets - definiteTargets) + " files to be recompiled from dependency analysis.");
}
log(Integer.toString(currentTargets) + " total files to be compiled.");
return currentTargets;
}
protected LinkerConfiguration collectExplicitObjectFiles(final Vector objectFiles,
final Vector sysObjectFiles, final VersionInfo versionInfo) {
//
// find the first eligible linker
//
//
ProcessorConfiguration linkerConfig = null;
LinkerDef selectedLinkerDef = null;
Linker selectedLinker = null;
final Hashtable sysLibraries = new Hashtable<>();
final TargetDef targetPlatform = getTargetPlatform();
FileVisitor objCollector = null;
FileVisitor sysLibraryCollector = null;
for (int i = 0; i < this._linkers.size(); i++) {
final LinkerDef currentLinkerDef = this._linkers.elementAt(i);
if (currentLinkerDef.isActive()) {
selectedLinkerDef = currentLinkerDef;
selectedLinker = currentLinkerDef.getProcessor().getLinker(this.linkType);
//
// skip the linker if it doesn't know how to
// produce the specified link type
if (selectedLinker != null) {
linkerConfig = currentLinkerDef.createConfiguration(this, this.linkType, this.linkerDef, targetPlatform,
versionInfo);
if (linkerConfig != null) {
//
// create collectors for object files
// and system libraries
objCollector = new ObjectFileCollector(selectedLinker, objectFiles);
sysLibraryCollector = new SystemLibraryCollector(selectedLinker, sysLibraries);
//
// if the has embedded 's
// (such as linker specific libraries)
// add them as object files.
//
if (currentLinkerDef.hasFileSets()) {
currentLinkerDef.visitFiles(objCollector);
}
//
// user libraries are just a specialized form
// of an object fileset
selectedLinkerDef.visitUserLibraries(selectedLinker, objCollector);
}
break;
}
}
}
if (linkerConfig == null) {
linkerConfig = this.linkerDef.createConfiguration(this, this.linkType, null, targetPlatform, versionInfo);
selectedLinker = this.linkerDef.getProcessor().getLinker(this.linkType);
objCollector = new ObjectFileCollector(selectedLinker, objectFiles);
sysLibraryCollector = new SystemLibraryCollector(selectedLinker, sysLibraries);
}
//
// unless there was a element that
// explicitly did not inherit files from
// containing element
if (selectedLinkerDef == null || selectedLinkerDef.getInherit()) {
this.linkerDef.visitUserLibraries(selectedLinker, objCollector);
this.linkerDef.visitSystemLibraries(selectedLinker, sysLibraryCollector);
}
//
// if there was a in a nested
// evaluate it last so it takes priority over
// identically named libs from element
//
if (selectedLinkerDef != null) {
//
// add any system libraries to the hashtable
// done in reverse order so the earliest
// on the classpath takes priority
selectedLinkerDef.visitSystemLibraries(selectedLinker, sysLibraryCollector);
}
//
// copy over any system libraries to the
// object files vector
//
final Enumeration sysLibEnum = sysLibraries.elements();
while (sysLibEnum.hasMoreElements()) {
sysObjectFiles.addElement(sysLibEnum.nextElement());
}
return (LinkerConfiguration) linkerConfig;
}
/**
* Adds an include path.
*
* Include paths will be inherited by nested compiler elements that do not
* have inherit="false".
*/
public IncludePath createIncludePath() {
return this.compilerDef.createIncludePath();
}
/**
* Specifies precompilation prototype file and exclusions. Inherited by all
* compilers that do not have inherit="false".
*
*/
public PrecompileDef createPrecompile() throws BuildException {
return this.compilerDef.createPrecompile();
}
/**
* Adds a system include path. Locations and timestamps of files located
* using the system include paths are not used in dependency analysis.
*
*
* Standard include locations should not be specified. The compiler
* adapters should recognized the settings from the appropriate environment
* variables or configuration files.
*
* System include paths will be inherited by nested compiler elements that
* do not have inherit="false".
*/
public SystemIncludePath createSysIncludePath() {
return this.compilerDef.createSysIncludePath();
}
// ENDFREEHEP
/**
* Executes the task. Compiles the given files.
*
* @throws BuildException
* if someting goes wrong with the build
*/
@Override
public void execute() throws BuildException {
//
// if link type allowed objdir to be defaulted
// provide it from outfile
if (this._objDir == null) {
if (this._outfile != null) {
this._objDir = new File(this._outfile.getParent());
} else {
this._objDir = new File(".");
}
}
//
// if the object directory does not exist
//
if (!this._objDir.exists()) {
throw new BuildException("Object directory does not exist");
}
final TargetHistoryTable objHistory = new TargetHistoryTable(this, this._objDir);
//
// get the first active version info
//
VersionInfo versionInfo = null;
final Enumeration versionEnum = this.versionInfos.elements();
while (versionEnum.hasMoreElements()) {
versionInfo = versionEnum.nextElement();
versionInfo = versionInfo.merge();
if (versionInfo.isActive()) {
break;
} else {
versionInfo = null;
}
}
//
// determine the eventual linker configuration
// (may be null) and collect any explicit
// object files or libraries
final Vector objectFiles = new Vector<>();
final Vector sysObjectFiles = new Vector<>();
final LinkerConfiguration linkerConfig = collectExplicitObjectFiles(objectFiles, sysObjectFiles, versionInfo);
//
// Assemble hashtable of all files
// that we know how to compile (keyed by output file name)
//
final Map targets = getTargets(linkerConfig, objectFiles, versionInfo, this._outfile);
TargetInfo linkTarget = null;
//
// if output file is not specified,
// then skip link step
//
if (this._outfile != null) {
linkTarget = getLinkTarget(linkerConfig, objectFiles, sysObjectFiles, targets, versionInfo);
}
if (this.projects.size() > 0) {
final List files = new ArrayList<>();
final ProjectFileCollector matcher = new ProjectFileCollector(files);
for (int i = 0; i < this._compilers.size(); i++) {
final CompilerDef currentCompilerDef = this._compilers.elementAt(i);
if (currentCompilerDef.isActive() && currentCompilerDef.hasFileSets()) {
currentCompilerDef.visitFiles(matcher);
}
}
this.compilerDef.visitFiles(matcher);
final Enumeration iter = this.projects.elements();
while (iter.hasMoreElements()) {
final ProjectDef projectDef = iter.nextElement();
if (projectDef.isActive()) {
projectDef.execute(this, files, targets, linkTarget);
}
}
}
if (this.projectsOnly) {
return;
}
//
// mark targets that don't have a history record or
// whose source last modification time is not
// the same as the history to be rebuilt
//
objHistory.markForRebuild(targets);
final CCTaskProgressMonitor monitor = new CCTaskProgressMonitor(objHistory, versionInfo);
//
// check for changed include files
//
final int rebuildCount = checkForChangedIncludeFiles(targets);
if (rebuildCount > 0) {
BuildException compileException = null;
//
// compile all targets with getRebuild() == true
//
final Map> targetsByConfig = getTargetsToBuildByConfiguration(targets);
//
// build array containing Vectors with precompiled generation
// steps going first
//
final ArrayList> targetVectorsPreComp = new ArrayList<>();
final ArrayList> targetVectors = new ArrayList<>();
int index = 0;
for (final Map.Entry> targetsForConfig : targetsByConfig.entrySet()) {
//
// get the configuration from the first entry
//
final CompilerConfiguration config = targetsForConfig.getKey();
if (config.isPrecompileGeneration()) {
targetVectorsPreComp.add(targetsForConfig.getValue());
} else {
targetVectors.add(targetsForConfig.getValue());
}
}
// BEGINFREEHEP
final Progress progress = new Progress(getObjdir(), rebuildCount);
progress.start();
// ENDFREEHEP
compileException = runTargetPool(monitor, compileException, targetVectorsPreComp);
if (compileException == null || this.relentless)
compileException = runTargetPool(monitor, compileException, targetVectors);
// BEGINFREEHEP
progress.exit();
try {
progress.join();
} catch (final InterruptedException ex) {
}
// ENDFREEHEP
//
// save the details of the object file compilation
// settings to disk for dependency analysis
//
try {
objHistory.commit();
} catch (final IOException ex) {
this.log("Error writing history.xml: " + ex.toString());
}
//
// if we threw a compile exception and
// didn't throw it at the time because
// we were relentless then
// save the history and
// throw the exception
//
if (compileException != null) {
if (this.failOnError) {
throw compileException;
} else {
log(compileException.getMessage(), Project.MSG_ERR);
return;
}
}
}
//
// if the dependency tree was not fully
// evaluated, then throw an exception
// since we really didn't do what we
// should have done
//
//
if (this.dependencyDepth >= 0) {
throw new BuildException("All files at depth " + Integer.toString(this.dependencyDepth)
+ " from changes successfully compiled.\n"
+ "Remove or change dependencyDepth to -1 to perform full compilation.");
}
//
// if no link target then
// commit the history for the object files
// and leave the task
if (linkTarget != null) {
//
// get the history for the link target (may be the same
// as the object history)
final TargetHistoryTable linkHistory = getLinkHistory(objHistory);
//
// see if it needs to be rebuilt
//
linkHistory.markForRebuild(linkTarget);
//
// if it needs to be rebuilt, rebuild it
//
final File output = linkTarget.getOutput();
if (linkTarget.getRebuild()) {
final LinkerConfiguration linkConfig = (LinkerConfiguration) linkTarget.getConfiguration();
// BEGINFREEHEP
log("Linking...");
log("Starting link {" + linkConfig.getIdentifier() + "}");
// ENDFREEHEP
if (this.failOnError) {
linkConfig.link(this, linkTarget);
} else {
try {
linkConfig.link(this, linkTarget);
} catch (final BuildException ex) {
log(ex.getMessage(), Project.MSG_ERR);
return;
}
}
if (this.outputFileProperty != null) {
getProject().setProperty(this.outputFileProperty, output.getAbsolutePath());
}
linkHistory.update(linkTarget);
try {
linkHistory.commit();
} catch (final IOException ex) {
log("Error writing link history.xml: " + ex.toString());
}
} else {
if (this.outputFileProperty != null) {
getProject().setProperty(this.outputFileProperty, output.getAbsolutePath());
}
}
// If sharedObjectName was specified, add the shared object to an archive, then delete the shared object.
if (sharedObjectName != null && !sharedObjectName.isEmpty()) {
File workingDirectory = new File(this._outfile.getParent());
String[] archiveCommand = new String[4];
archiveCommand[0] = "ar";
archiveCommand[1] = "r";
archiveCommand[2] = linkerConfig.getOutputFileNames(this._outfile.getName(), versionInfo)[0];
archiveCommand[3] = sharedObjectName;
CUtil.runCommand(this, workingDirectory, archiveCommand, false, null);
String[] removeCommand = new String[2];
removeCommand[0] = "rm";
removeCommand[1] = sharedObjectName;
CUtil.runCommand(this, workingDirectory, removeCommand, false, null);
}
}
}
private BuildException runTargetPool(final CCTaskProgressMonitor monitor, BuildException compileException,
final ArrayList> targetVectors) {
int index;
for (final Vector targetsForConfig : targetVectors) {
//
// get the configuration from the first entry
//
final CompilerConfiguration config = (CompilerConfiguration) targetsForConfig.elementAt(0).getConfiguration();
//
// prepare the list of source files
//
// BEGINFREEHEP
int noOfCores = Runtime.getRuntime().availableProcessors();
log("Found " + noOfCores + " processors available");
if (this.maxCores > 0) {
noOfCores = Math.min(this.maxCores, noOfCores);
log("Limited processors to " + noOfCores);
}
final int noOfFiles = targetsForConfig.size();
if (noOfFiles < noOfCores) {
noOfCores = noOfFiles;
log("Limited used processors to " + noOfCores);
}
if (this.ordered) {
noOfCores = 1;
log("Limited processors to 1 due to ordering of source files");
}
final List[] sourceFiles = new List[noOfCores];
for (int j = 0; j < sourceFiles.length; j++) {
sourceFiles[j] = new ArrayList<>(noOfFiles / sourceFiles.length);
}
final Enumeration targetsEnum = targetsForConfig.elements();
index = 0;
while (targetsEnum.hasMoreElements()) {
final TargetInfo targetInfo = targetsEnum.nextElement();
sourceFiles[index++].add(targetInfo.getSources()[0].toString());
index %= sourceFiles.length;
}
// setup cores/cpus
final Core[] cores = new Core[noOfCores];
for (int j = 0; j < cores.length; j++) {
cores[j] = new Core(this, j, config, this._objDir, sourceFiles[j], this.relentless, monitor);
log("\nStarting Core " + j + " with " + sourceFiles[j].size() + " source files...");
}
// starting cores
for (final Core core : cores) {
core.start();
}
// checking cores
boolean alive = false;
try {
do {
alive = false;
for (int j = 0; j < cores.length; j++) {
if (cores[j] != null) {
if (cores[j].isAlive()) {
alive = true;
} else {
final Exception exception = cores[j].getException();
if (exception != null) {
if (compileException == null && exception instanceof BuildException) {
compileException = (BuildException) exception;
} else {
log(cores[j].getName() + " " + exception + " ", Project.MSG_ERR);
}
if (!this.relentless) {
cores[j] = null;
alive = false;
break;
}
}
cores[j] = null;
}
}
}
if (alive) {
// wait for a maximum of 5 seconds or #files*2 seconds.
Thread.sleep(Math.min(5000, sourceFiles[0].size() * 2000));
}
} while (alive);
} catch (final InterruptedException e) {
break;
}
// killing leftovers
for (final Core core : cores) {
if (core != null) {
core.interrupt();
log(core.getName() + " interrupted ");
}
}
if (!this.relentless && compileException != null) {
break;
}
// ENDFREEHEP
/*
* OLD CODE
* String[] sourceFiles = new String[targetsForConfig.size()];
* Enumeration targetsEnum = targetsForConfig.elements();
* index = 0;
* while (targetsEnum.hasMoreElements()) {
* TargetInfo targetInfo = ((TargetInfo) targetsEnum
* .nextElement());
* sourceFiles[index++] = targetInfo.getSources()[0]
* .toString();
* }
* try {
* config.compile(this, _objDir, sourceFiles, relentless,
* monitor);
* } catch (BuildException ex) {
* if (compileException == null) {
* compileException = ex;
* }
* if (!relentless)
* break;
* }
*/
}
return compileException;
}
// ENDFREEHEP
/**
* Get the commandLogLevel
*
* @return The current commandLogLevel
*/
public int getCommandLogLevel() {
return this.commandLogLevel;
}
/**
* Gets the dataset.
*
* @return Returns a String
*/
public String getDataset() {
return this.dataset;
}
/**
* Gets debug state.
*
* @return true if building for debugging
*/
public boolean getDebug() {
return this.compilerDef.getDebug(null, 0);
}
/**
* Gets the failonerror flag.
*
* @return the failonerror flag
*/
public boolean getFailonerror() {
return this.failOnError;
}
protected TargetHistoryTable getLinkHistory(final TargetHistoryTable objHistory) {
final File outputFileDir = new File(this._outfile.getParent());
//
// if the output file is being produced in the link
// directory, then we can use the same history file
//
if (this._objDir.equals(outputFileDir)) {
return objHistory;
}
return new TargetHistoryTable(this, outputFileDir);
}
protected TargetInfo getLinkTarget(final LinkerConfiguration linkerConfig, final Vector objectFiles,
final Vector sysObjectFiles, final Map compileTargets, final VersionInfo versionInfo) {
//
// walk the compile phase targets and
// add those sources that have already been
// assigned to the linker or
// our output files the linker knows how to consume
// files the linker knows how to consume
//
for (final TargetInfo compileTarget : compileTargets.values()) {
//
// output of compile tasks
//
final int bid = linkerConfig.bid(compileTarget.getOutput().toString());
if (bid > 0) {
objectFiles.addElement(compileTarget.getOutput());
}
}
final File[] objectFileArray = new File[objectFiles.size()];
objectFiles.copyInto(objectFileArray);
final File[] sysObjectFileArray = new File[sysObjectFiles.size()];
sysObjectFiles.copyInto(sysObjectFileArray);
File outputFile;
if (sharedObjectName == null || sharedObjectName.isEmpty()) {
final String baseName = this._outfile.getName();
final String[] fullNames = linkerConfig.getOutputFileNames(baseName, versionInfo);
outputFile = new File(this._outfile.getParent(), fullNames[0]);
} else {
outputFile = new File(this._outfile.getParent(), sharedObjectName);
}
return new TargetInfo(linkerConfig, objectFileArray, sysObjectFileArray, outputFile, linkerConfig.getRebuild());
}
public int getMaxCores() {
return this.maxCores;
}
public File getObjdir() {
return this._objDir;
}
public File getOutfile() {
return this._outfile;
}
/**
* Gets output type.
*
* @return output type
*/
public String getOuttype() {
return this.linkType.getOutputType();
}
/**
* Gets subsystem name.
*
* @return Subsystem name
*/
public String getSubsystem() {
return this.linkType.getSubsystem();
}
public TargetDef getTargetPlatform() {
return null;
}
/**
* This method collects a Hashtable, keyed by output file name, of
* TargetInfo's for every source file that is specified in the filesets of
* the and nested elements. The TargetInfo's contain the
* appropriate compiler configurations for their possible compilation
*
*/
private Map getTargets(final LinkerConfiguration linkerConfig, final Vector objectFiles,
final VersionInfo versionInfo, final File outputFile) {
// FREEHEP
final List order = new ArrayList<>();
final Map targets = new TreeMap<>(new Comparator() {
// Order according to "order" List followed by alphabetical order
@Override public int compare(String f0, String f1) {
if (order.isEmpty()) {
return f0.compareTo(f1);
}
// Trimming the path and trailing file extension to allow for order
// comparison
String compf0 = FilenameUtils.getBaseName(f0);
String compf1 = FilenameUtils.getBaseName(f1);
// remove the hash
// TODO: well we hope it's a hash
compf0 = FilenameUtils.removeExtension(compf0);
compf1 = FilenameUtils.removeExtension(compf1);
// order according to list or alphabetical
final int i0 = order.indexOf(compf0);
final int i1 = order.indexOf(compf1);
if (i0 < 0 && i1 < 0) {
// none in list
// compare original values
return f0.compareTo(f1);
} else {
// make sure we use only one core
CCTask.this.ordered = true;
if (i0 > 0 && i1 > 0) {
// both in list
return i0 == i1 ? 0 : i0 < i1 ? -1 : +1;
} else if (i1 < 0) {
// i0 in list
return -1;
} else {
// i1 in list
return +1;
}
}
}
});
final TargetDef targetPlatform = getTargetPlatform();
// BEGINFREEHEP
// a little trick here, the inner function needs the list to be final,
// so that the map order doesn't change after we start adding items,
// populate with all the ordered items from each compiler type
order.clear();
for (int i = 0; i < this._compilers.size(); i++) {
final CompilerDef currentCompilerDef = this._compilers.elementAt(i);
if (currentCompilerDef.isActive()) {
final List compilerFileOrder = currentCompilerDef.getOrder();
if (compilerFileOrder != null) {
order.addAll(compilerFileOrder);
}
}
}
// ENDFREEHEP
//
// find active (specialized) compilers
//
final Vector biddingProcessors = new Vector<>(this._compilers.size());
for (int i = 0; i < this._compilers.size(); i++) {
final CompilerDef currentCompilerDef = this._compilers.elementAt(i);
if (currentCompilerDef.isActive()) {
final ProcessorConfiguration config = currentCompilerDef.createConfiguration(this, this.linkType,
this.compilerDef, targetPlatform, versionInfo);
//
// see if this processor had a precompile child element
//
final PrecompileDef precompileDef = currentCompilerDef.getActivePrecompile(this.compilerDef);
CommandLineCompilerConfiguration commandLineConfig = (CommandLineCompilerConfiguration) config;
AbstractCompiler compiler = (AbstractCompiler) commandLineConfig.getCompiler();
compiler.setWorkDir(currentCompilerDef.getWorkDir());
compiler.setGccFileAbsolutePath(currentCompilerDef.getGccFileAbsolutePath());
ProcessorConfiguration[] localConfigs = new ProcessorConfiguration[] {
config
};
//
// if it does then
//
if (precompileDef != null) {
final File prototype = precompileDef.getPrototype();
//
// will throw exceptions if prototype doesn't exist, etc
//
if (!prototype.exists()) {
throw new BuildException("prototype (" + prototype.toString() + ") does not exist.");
}
if (prototype.isDirectory()) {
throw new BuildException("prototype (" + prototype.toString() + ") is a directory.");
}
final String[] exceptFiles = precompileDef.getExceptFiles();
//
// create a precompile building and precompile using
// variants of the configuration
// or return null if compiler doesn't support
// precompilation
final CompilerConfiguration[] configs = ((CompilerConfiguration) config).createPrecompileConfigurations(
prototype, exceptFiles);
if (configs != null && configs.length == 2) {
//
// visit the precompiled file to add it into the
// targets list (just like any other file if
// compiler doesn't support precompilation)
final TargetMatcher matcher = new TargetMatcher(this, this._objDir, new ProcessorConfiguration[] {
configs[0]
}, linkerConfig, objectFiles, targets, versionInfo);
matcher.visit(new File(prototype.getParent()), prototype.getName());
//
// only the configuration that uses the
// precompiled header gets added to the bidding list
biddingProcessors.addElement(configs[1]);
localConfigs = new ProcessorConfiguration[2];
localConfigs[0] = configs[1];
localConfigs[1] = config;
}
}
//
// if the compiler has a fileset
// then allow it to add its files
// to the set of potential targets
if (currentCompilerDef.hasFileSets()) {
final TargetMatcher matcher = new TargetMatcher(this, this._objDir, localConfigs, linkerConfig, objectFiles,
targets, versionInfo);
currentCompilerDef.visitFiles(matcher);
}
biddingProcessors.addElement(config);
}
}
//
// add fallback compiler at the end
//
if (this._compilers.size()==0) {
final ProcessorConfiguration config = this.compilerDef.createConfiguration(this, this.linkType, null,
targetPlatform, versionInfo);
biddingProcessors.addElement(config);
}
final ProcessorConfiguration[] bidders = new ProcessorConfiguration[biddingProcessors.size()];
biddingProcessors.copyInto(bidders);
//
// bid out the 's in the cctask
//
final TargetMatcher matcher = new TargetMatcher(this, this._objDir, bidders, linkerConfig, objectFiles, targets,
versionInfo);
this.compilerDef.visitFiles(matcher);
//Add the VersionInfo when relevant
if (outputFile != null && versionInfo != null) {
final boolean isDebug = linkerConfig.isDebug();
try {
linkerConfig.getLinker()
.addVersionFiles(versionInfo, this.linkType, outputFile, isDebug, this._objDir, matcher);
} catch (final IOException ex) {
throw new BuildException(ex);
}
}
return targets;
}
public boolean isDecorateLinkerOptions() {
return this.decorateLinkerOptions;
}
/**
* Sets the default compiler adapter. Use the "name" attribute when the
* compiler is a supported compiler.
*
* @param classname
* fully qualified classname which implements CompilerAdapter
*/
public void setClassname(final String classname) {
this.compilerDef.setClassname(classname);
this.linkerDef.setClassname(classname);
}
/**
* Set commandLogLevel
*
* ( CUtil.runCommand() will honor this... )
*
* @param commandLogLevel
* The log-level for command-logs, default is MSG_VERBOSE.
*/
public void setCommandLogLevel(final int commandLogLevel) {
this.commandLogLevel = commandLogLevel;
}
/**
* Sets the dataset for OS/390 builds.
*
* @param dataset
* The dataset to set
*/
public void setDataset(final String dataset) {
this.dataset = dataset;
}
/**
* Enables or disables generation of debug info.
*/
public void setDebug(final boolean debug) {
this.compilerDef.setDebug(debug);
this.linkerDef.setDebug(debug);
}
public void setDecorateLinkerOptions(final boolean decorateLinkerOptions) {
this.decorateLinkerOptions = decorateLinkerOptions;
}
/**
* Deprecated.
*
* Controls the depth of the dependency evaluation. Used to do a quick
* check of changes before a full build.
*
* Any negative value which will perform full dependency checking. Positive
* values will truncate dependency checking. A value of 0 will cause only
* those files that changed to be recompiled, a value of 1 which cause
* files that changed or that explicitly include a file that changed to be
* recompiled.
*
* Any non-negative value will cause a BuildException to be thrown before
* attempting a link or completing the task.
*
*/
public void setDependencyDepth(final int depth) {
this.dependencyDepth = depth;
}
/**
* Enables generation of exception handling code
*/
public void setExceptions(final boolean exceptions) {
this.compilerDef.setExceptions(exceptions);
}
/**
* Indicates whether the build will continue
* even if there are compilation errors; defaults to true.
*
* @param fail
* if true halt the build on failure
*/
public void setFailonerror(final boolean fail) {
this.failOnError = fail;
}
// public LinkType getLinkType() {
// return linkType;
// }
/**
* Enables or disables incremental linking.
*
* @param incremental
* new state
*/
public void setIncremental(final boolean incremental) {
this.linkerDef.setIncremental(incremental);
}
/**
* Set use of libtool.
*
* If set to true, the "libtool " will be prepended to the command line for
* compatible processors
*
* @param libtool
* If true, use libtool.
*/
public void setLibtool(final boolean libtool) {
this.compilerDef.setLibtool(libtool);
this.linkerDef.setLibtool(libtool);
}
/**
* Sets the output file type. Supported values "executable", "shared", and
* "static". Deprecated, specify outtype instead.
*
* @deprecated
*/
@Deprecated
public void setLink(final OutputTypeEnum outputType) {
this.linkType.setOutputType(outputType);
}
// BEGINFREEHEP
public void setLinkCPP(final boolean linkCPP) {
this.linkType.setLinkCPP(linkCPP);
}
public void setLinkFortran(final boolean linkFortran) {
this.linkType.setLinkFortran(linkFortran);
}
public void setLinkFortranMain(final boolean linkFortranMain) {
this.linkType.setLinkFortranMain(linkFortranMain);
}
// ENDFREEHEP
// BEGINFREEHEP
public void setMaxCores(final int maxCores) {
this.maxCores = maxCores;
}
/**
* Enables or disables generation of multithreaded code
*
* @param multi
* If true, generated code may be multithreaded.
*/
public void setMultithreaded(final boolean multi) {
this.compilerDef.setMultithreaded(multi);
}
//
// keep near duplicate comment at CompilerDef.setName in sync
//
/**
* Sets type of the default compiler and linker.
*
*
* Supported compilers
*
* gcc (default)
* GCC C++ compiler
*
*
* g++
* GCC C++ compiler
*
*
* c++
* GCC C++ compiler
*
*
* g77
* GNU FORTRAN compiler
*
*
* msvc
* Microsoft Visual C++
*
*
* bcc
* Borland C++ Compiler
*
*
* msrc
* Microsoft Resource Compiler
*
*
* brc
* Borland Resource Compiler
*
*
* df
* Compaq Visual Fortran Compiler
*
*
* midl
* Microsoft MIDL Compiler
*
*
* icl
* Intel C++ compiler for Windows (IA-32)
*
*
* ecl
* Intel C++ compiler for Windows (IA-64)
*
*
* icc
* Intel C++ compiler for Linux (IA-32)
*
*
* ecc
* Intel C++ compiler for Linux (IA-64)
*
*
* CC
* Sun ONE C++ compiler
*
*
* aCC
* HP aC++ C++ Compiler
*
*
* os390
* OS390 C Compiler
*
*
* os400
* Icc Compiler
*
*
* sunc89
* Sun C89 C Compiler
*
*
* xlC
* VisualAge C Compiler
*
*
* uic
* Qt user interface compiler (creates .h, .cpp and moc_*.cpp files).
*
*
* moc
* Qt meta-object compiler
*
*
* xpidl
* Mozilla xpidl compiler (creates .h and .xpt files).
*
*
* wcl
* OpenWatcom C/C++ compiler
*
*
* wfl
* OpenWatcom FORTRAN compiler
*
*
*
*/
public void setName(final CompilerEnum name) {
this.compilerDef.setName(name);
final Processor compiler = this.compilerDef.getProcessor();
final Linker linker = compiler.getLinker(this.linkType);
this.linkerDef.setProcessor(linker);
}
/**
* Do not propagate old environment when new environment variables are
* specified.
*/
public void setNewenvironment(final boolean newenv) {
this.compilerDef.setNewenvironment(newenv);
for (int i = 0; i < this._compilers.size(); i++) {
final CompilerDef currentCompilerDef = this._compilers.elementAt(i);
currentCompilerDef.setNewenvironment(newenv);
}
this.linkerDef.setNewenvironment(newenv);
}
/**
* Sets the destination directory for object files.
*
* Generally this should be a property expression that evaluates to
* distinct debug and release object file directories.
*
* @param dir
* object directory
*/
public void setObjdir(final File dir) {
if (dir == null) {
throw new NullPointerException("dir");
}
this._objDir = dir;
}
/**
* Sets optimization.
*
* @param optimization
*/
public void setOptimize(final OptimizationEnum optimization) {
this.compilerDef.setOptimize(optimization);
}
/**
* Sets the output file name. If not specified, the task will only compile
* files and not attempt to link. If an extension is not specified, the
* task may use a system appropriate extension and prefix, for example,
* outfile="example" may result in "libexample.so" being created.
*
* @param outfile
* output file name
*/
public void setOutfile(final File outfile) {
//
// if file name was empty, skip link step
//
if (outfile == null || outfile.toString().length() > 0) {
this._outfile = outfile;
}
}
/**
* Specifies the name of a property to set with the physical filename that
* is produced by the linker
*/
public void setOutputFileProperty(final String outputFileProperty) {
this.outputFileProperty = outputFileProperty;
}
/**
* Sets the output file type. Supported values "executable", "shared", and
* "static".
*/
public void setOuttype(final OutputTypeEnum outputType) {
this.linkType.setOutputType(outputType);
}
// ENDFREEHEP
/**
* Sets the project.
*/
@Override
public void setProject(final Project project) {
super.setProject(project);
this.compilerDef.setProject(project);
this.linkerDef.setProject(project);
}
public void setProjectsOnly(final boolean value) {
this.projectsOnly = value;
}
/**
* If set to true, all files will be rebuilt.
*
* @param rebuildAll
* If true, all files will be rebuilt. If false, up to
* date files will not be rebuilt.
*/
public void setRebuild(final boolean rebuildAll) {
this.compilerDef.setRebuild(rebuildAll);
this.linkerDef.setRebuild(rebuildAll);
}
/**
* If set to true, compilation errors will not stop the task until all
* files have been attempted.
*
* @param relentless
* If true, don't stop on the first compilation error
*
*/
public void setRelentless(final boolean relentless) {
this.relentless = relentless;
}
/**
* Enables run-time type information.
*/
public void setRtti(final boolean rtti) {
this.compilerDef.setRtti(rtti);
}
/**
* Sets the type of runtime library, possible values "dynamic", "static".
*/
public void setRuntime(final RuntimeType rtlType) {
this.linkType.setStaticRuntime(rtlType.getIndex() == 1);
}
/**
* Sets the nature of the subsystem under which that the program will
* execute.
*
*
* Supported subsystems
*
* gui
* Graphical User Interface
*
*
* console
* Command Line Console
*
*
* other
* Other
*
*
*
* @param subsystem
* subsystem
* @throws NullPointerException
* if subsystem is null
*/
public void setSubsystem(final SubsystemEnum subsystem) {
if (subsystem == null) {
throw new NullPointerException("subsystem");
}
this.linkType.setSubsystem(subsystem);
}
/**
* Enumerated attribute with the values "none", "severe", "default",
* "production", "diagnostic", and "aserror".
*/
public void setWarnings(final WarningLevelEnum level) {
this.compilerDef.setWarnings(level);
}
/**
* @param sharedObjectName the sharedObjectName to set
*/
public void setSharedObjectName(String sharedObjectName) {
this.sharedObjectName = sharedObjectName;
}
}