blob: e5100f109adacf4cde73e0cdddf304350de3c47c [file] [log] [blame]
/* --COPYRIGHT--,EPL
* Copyright (c) 2008 Texas Instruments 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:
* Texas Instruments - initial implementation
*
* --/COPYRIGHT--*/
/*
* ======== _gen.xs ========
* Makefile generation code
*
* Exported methods
* getDir - add a file's directory to the specified hash
* mkMake - generate the makefile
* mkPkgBuild - generate build data incorporated into package schema
* mkProj - generate debug project files
* mkXML - dump build model for examination by other tools
*
* Private methods
* _genCfg
* _getArchiveOpts
* _getCompileOpts
* _getLinkOpts
* _inPkg
* _inThisPackage
* _mkBanner
* _mkDefs
* _mkDirs
* _mkDll
* _mkLib
* _mkManifest
* _mkObjs
* _mkProg
* _mkRelease
* _mkRepository
* _mkRule
* _mkScript
* _mkScriptTests
* _mkSchema
* _mkTests
*/
var debug = function () {};
var _Clock = xdc.module("xdc.services.global.Clock");
//_Clock.enable = true;
/*
* ======== getDir ========
* For the file specified, add its directory prefix to the dirs hash
*/
function getDir(file, dirs)
{
if (file.lastIndexOf('/') != -1 || file.lastIndexOf('\\') != -1) {
var dir = file.replace(/[\/\\][^\/\\]*$/, "");
if (dir != "") {
dirs[dir] = dir;
}
}
}
/*
* ======== mkMake ========
* For the PackageContents object pkg, gen makefile to the output stream out
*
* Portable makefiles (the ability to generate the makefile once and use it
* even though the location of compilers changes and external prerequisites
* changes) requires that:
* we avoid the generation of absolute paths in rules,
* all absolute paths in dependencies have an "empty rule"
*
* Since dependencies are recomputed as part of the compilation step, these
* absolute paths are "self correcting". The only exception is for
* dependencies that would trigger a regeneration of the makefile, but for
* portable makefiles we are assuming that no change requires rebuild of the
* the makefile. So, we simply need a way to forcibly not rebuild the
* makefile.
*
* Eliminating absolute paths in rules requires changes to the targets to
* use user-defined symbols in-lieu of paths; e.g., target.rootDir.
*
* To support portability between W32 and *nix, we need to generate symbols
* for Windows drive letters (to allow them to be mapped to *nix directories.
*/
function mkMake(pkg, out)
{
var i;
/* generate "this is a generated file" banner */
_mkBanner(out);
debug("generating package's makefile ...");
if (pkg.makePrologue != null) {
out.write(pkg.makePrologue + "\n");
}
/* Disable the export of the MAKE built-in variable MAKEFILE_LIST. This
* variable contains the list of all makefiles read by MAKE. Since we
* generate dependencies in separate files (to support parallel builds)
* this list can get quite long. So what? It often causes make to be
* unable to exec a process because the environment exceeds OS limits;
* Windows has a limit of 32K, Solaris 1Mb, and Linux 130K.
*/
out.write("unexport MAKEFILE_LIST\n");
/*
* MK_NOGENDEPS can be used to disable the inclusion of generated
* dependency files. This can speed builds that are run immediately
* after a clean _IF_ two separate builds occur _concurrently_ in the
* same directory; this questionable use case is described in
* 'Bug 310255 - unable to disable dependency generation'.
*/
out.write("MK_NOGENDEPS := $(filter clean,$(MAKECMDGOALS))\n");
out.write("override PKGDIR = " + pkg.name.replace(/\./g, '/') + "\n");
/* we remove this definition to allow xdcdir to define the package's
* repository; xdc_rules.mak already defines PKGROOT using this
* external command and this command can find a package's repository
* in the face of symbolic links which may fail with simple '../'
* paths.
*/
if (false) {
out.write("override PKGROOT = ..");
for (var i = pkg.name.split('.').length; i > 1; i--) {
out.write("/..");
}
out.write("\n");
}
/* if package path is specified in build script, override XDCPATH */
if (pkg.packagePath != null) {
/* $(PKGPATHSUFFIX) names XDC system package repositories */
out.write("PKGPATH := " + pkg.packagePath + ";$(PKGPATHSUFFIX)\n");
}
/*
* Define XDCINCS: directories that contain packages whose headers
* are included as "unqualified" headers; i.e., no package directory
* name is supplied (e.g., bios, dais, and csl headers)
*
* TODO: we should probably check for the existence of the directories
* in the uses set; if they are not found we should issue a warning.
*/
out.write("XDCINCS = -I. -I$(strip $(subst ;, -I,$(subst $(space),\\$(space),$(XPKGPATH))))\n");
var plist = "";
for (var i = 0; i < pkg.uses.length; i++) {
plist = plist.concat(pkg.uses[i] + " ");
}
if (plist != "") {
out.write("XDCPKGS = $(call pkgsearch," + plist + ")\n");
out.write("XDCINCS += $(if $(XDCPKGS),");
out.write("-I$(subst $(space), -I,$(XDCPKGS)))\n");
}
/* Define XDCCFGDIR: the directory that contains generated prog objects */
out.write("XDCCFGDIR = " + pkg.cfgDir + "\n\n");
/* Create dependencies on all included BOM scripts */
out.write("#\n# The following dependencies ensure package.mak is rebuilt\n");
out.write("# in the event that some included BOM script changes.\n#\n");
out.write("ifneq (clean,$(MAKECMDGOALS))\n");
var cwd = (new java.io.File(".")).getCanonicalPath()
+ java.io.File.separator;
for (var i in utils.loadedFiles) {
var cname = "" + (new java.io.File(i)).getCanonicalPath();
var fname;
if (_inPkg(cwd, cname)) {
/* Use relative path to prevent make from complaining about not
* knowing how to build this file in the case that we move this
* package to a different repository.
*/
fname = _escapeFilename(cname.substring(cwd.length));
}
else {
/* generate empty rule for files outside this package so that
* make won't complain about not knowing how to build the file
* in the case that we move the file's package to a different
* repository
*/
/* convert DOS '\' characters to UNIX-style '/' in file name */
fname = _escapeFilename(i);
/* TODO: replace absolute path with symbol in fname */
out.write(fname + ":\n");
}
out.write("package.mak: " + fname + "\n");
}
out.write("endif\n\n");
_Clock.print(" bld: package.mak gen: dependencies done.");
/* Define Target specific rules and dependencies for *only*
* those targets actually used in the package.bld script
*/
var targs = pkg.$private.targets;
var pb = {};
for (var i in targs) {
var targ = targs[i];
out.write(targ.$name + ".rootDir ?= " + targ.rootDir + "\n");
var pn = targ.$package.$name;
if (pb[pn] == null) {
out.write(pn + ".packageBase ?= "
+ targ.$package.packageBase + "\n");
pb[pn] = 1;
}
}
for (var i in targs) {
out.write(".PRECIOUS: $(XDCCFGDIR)/%.o" + i + "\n");
out.write(".PHONY: all," + i + " .dlls," + i
+ " .executables," + i + " test," + i + "\n");
out.write("all," + i + ": .executables," + i + "\n");
out.write(".executables," + i + ": .libraries," + i + "\n");
out.write(".executables," + i + ": .dlls," + i + "\n");
out.write(".dlls," + i + ": .libraries," + i + "\n");
out.write(".libraries," + i + ": .interfaces\n");
out.write("\t@$(RM) $@\n");
out.write("\t@$(TOUCH) \"$@\"\n\n");
out.write(".help::\n");
out.write("\t@$(ECHO) xdc test," + i + "\n");
out.write("\t@$(ECHO) xdc .executables," + i + "\n");
out.write("\t@$(ECHO) xdc .libraries," + i + "\n");
out.write("\t@$(ECHO) xdc .dlls," + i + "\n\n");
}
/* Define target specific rules this package doesn't support/require.
* We do this to allow recursive makes to visit directories that have
* nothing to contibute; this simplifies "big" or top-level makes.
*/
var targets = pkg.$package.BuildEnvironment.targets;
for (var i = 0; i < targets.length; i++) {
var found = false;
for (var j in targs) {
if (targets[i].suffix == j) {
found = true;
for (var k = 0; k < targets.length; k++) {
if (k != i && targets[k].suffix == j) {
throw new Error("Target '"
+ targets[k].name + "'s suffix is not unique");
}
}
/* get version for each target and save it in target */
var res = targets[i].getVersion();
targets[i].$unseal("version");
if (res != null) {
targets[i].version = res;
}
else {
targets[i].version = targets[i].$name + '{';
}
targets[i].$seal("version");
/* get rawVersion for each target and save it in target */
var it3 = xdc.module("xdc.bld.ITarget3");
var t3 = it3.Module(targets[i]);
if (t3 != null) {
var rawVer = t3.getRawVersion();
t3.$unseal("rawVersion");
if (rawVer != null) {
t3.rawVersion = rawVer;
}
else {
t3.rawVersion = t3.$name;
}
t3.$seal("rawVersion");
}
_Clock.print(" bld: package.mak gen: version for "
+ targets[i].name + "("
+ targets[i].version + ") done.");
break;
}
}
if (found == false) {
out.write("all," + targets[i].suffix
+ " clean," + targets[i].suffix
+ " .libraries," + targets[i].suffix
+ " .dlls," + targets[i].suffix
+ " .executables," + targets[i].suffix
+ " test," + targets[i].suffix
+ ":;\n");
}
}
out.write("\n");
/* define the universal (Target independent) goals and dependencies */
out.write("all: .executables \n");
out.write(".executables: .libraries .dlls\n");
out.write(".libraries: .interfaces\n\n");
_Clock.print(" bld: package.mak gen: generic target rules done.");
/* generate package interface rules and dependencies */
_mkSchema(pkg, out);
/* generate all libraries */
for (var i in pkg.$private.libraries) {
_mkLib(pkg, pkg.$private.libraries[i], out);
}
_Clock.print(" bld: package.mak gen: library rules done.");
/* generate all dlls */
for (var i in pkg.$private.dlls) {
_mkDll(pkg, pkg.$private.dlls[i], out);
}
_Clock.print(" bld: package.mak gen: dll rules done.");
/* generate all assemblies */
for (var i in pkg.$private.assemblies) {
// _mkAsm(pkg, pkg.$private.assemblies[i], out);
}
/* generate all configurations */
for (var i in pkg.$private.configurations) {
_mkConfig(pkg, pkg.$private.configurations[i], out);
}
/* generate all executables (and tests) */
for (var i = 0; i < pkg.$private.executables.length; i++) {
var exe = pkg.$private.executables[i];
_mkProg2(pkg, exe, out);
}
_Clock.print(" bld: package.mak gen: executable rules done.");
/* generate all target filter macro definitions */
for (var i in targs) {
var targ = targs[i];
for (var pname in targ.profiles) {
var po = targ.profiles[pname];
for (var j = 0; j < po.filters.length; j++) {
var filter = po.filters[j].$private.filter;
/* only gen definitions for profiles actually used */
if (filter != null) {
var defs = filter.getDefs();
if (defs && defs.length > 0) {
out.write("\n# macro definitions from target filter"
+ targ.$name + ":"
+ po.filters[j].moduleName + "\n");
for (var k = 0; k < defs.length; k++) {
out.write(defs[k].name + " ?= " + defs[k].value +'\n');
}
out.write('\n');
}
}
}
}
}
/* generate at least one test goal; so that "make test" does not fail */
if (pkg.$private.executables.length <= 0) {
out.write("test:;\n");
}
/* generate all scripts */
for (var i in pkg.$private.scripts) {
_mkScript(pkg, pkg.$private.scripts[i], out);
}
_Clock.print(" bld: package.mak gen: test rules done.");
/* generate copy goals */
_mkCopyGoals(pkg, out);
/* Define pattern rule to run config tool (we need a pattern rule
* instead of explicit rules for each goal so that parallel make will
* not try to build each of these files in parallel.
*
* RTSC config generates three files from the config script foo.cfg:
* foo.c - module and instance data
* foo.xdl - linker command file for the executable
* foo.h - declarations of config data (e.g., global vars)
*/
out.write("\n");
out.write("$(XDCCFGDIR)%.c $(XDCCFGDIR)%.h $(XDCCFGDIR)%.xdl: $(XDCCFGDIR)%.cfg $(XDCROOT)/packages/xdc/cfg/Main.xs | .interfaces\n");
out.write('\t@$(MSG) "configuring $(_PROG_NAME) from $< ..."\n');
out.write('\t$(CONFIG) $(_PROG_XSOPTS) xdc.cfg $(_PROG_NAME) $(XDCCFGDIR)$*.cfg $(XDCCFGDIR)$*\n');
/* generate repositories; i.e., repository of other packages */
var relList = {};
for (var rname in pkg.$private.repositories) {
var repository = pkg.$private.repositories[rname];
_mkRepository(pkg, repository, out, relList);
}
_Clock.print(" bld: package.mak gen: repository rules done.");
/* generate documentation */
_mkDocs(pkg, out);
/* generate releases; i.e., archives of all exported files */
for (var rname in pkg.$private.releases) {
var release = pkg.$private.releases[rname];
_mkRelease(pkg, release, out);
}
_Clock.print(" bld: package.mak gen: release rules done.");
/* generate clean rule to remove .libraries and run default .clean rules */
out.write("clean:: .clean\n\t-$(RM) .libraries $(wildcard .libraries,*)\n");
out.write("clean:: \n\t-$(RM) .dlls $(wildcard .dlls,*)\n");
/* add clean rules for user specified generated files */
if ("generatedFiles" in pkg) {
out.write("#\n# The following clean rule removes user specified\n");
out.write("# generated files or directories.\n#\n");
for (var i = 0; i < pkg.generatedFiles.length; i++) {
var file = pkg.generatedFiles[i];
if (file.charAt(file.length - 1) == "/") {
out.write("\t-$(RMDIR) ");
}
else {
out.write("\t-$(RM) ");
}
out.write(file + "\n");
}
}
out.write("\n");
/* generate all sub-directories required by previous goals */
_mkDirs(pkg, out);
_Clock.print(" bld: package.mak gen: clean rules done.");
/* generate final custom steps specified and flush output to disk */
if (pkg.makeEpilogue != null) {
out.write(pkg.makeEpilogue + "\n");
}
out.flush();
}
/*
* ======== mkPkgBuild ========
* The file generated here is copied into the package's schema (unlike
* package.xs which is loaded at run time). This file contains
* "package-level" information we want/need to pass from the BOM to the
* TCOM.
*/
function mkPkgBuild(pkg, filename)
{
/* only add date in the Patch field of MSRP or later */
var cfg = "if (pkg.$vers.length >= 3) {\n pkg.$vers.push(Packages.xdc.services.global.Vers.getDate(xdc.csd() + '/..'));\n}\n\n";
var tab = " ";
/* pass the list of all libraries to the config domain. We use this
* list to determine the default getLibs() function; if there are
* no libraries, return null, otherwise return an appropriately mangled
* library name (from isa and package name).
*/
cfg = cfg.concat("pkg.build.libraries = [\n");
for (var i in pkg.$private.libraries) {
cfg = cfg.concat(tab + "'" + i + "',\n");
}
cfg = cfg.concat("];\n\n");
cfg = cfg.concat("pkg.build.libDesc = [\n");
for (var i in pkg.$private.libraries) {
cfg = cfg.concat(tab + "[\n");
cfg = cfg.concat(tab + tab + "'" + i + "',\n");
cfg = cfg.concat(tab + tab + "{\n");
cfg = cfg.concat(tab + tab + tab + "target: '" +
pkg.$private.libraries[i].target.$name + "',\n");
cfg = cfg.concat(tab + tab + tab + "suffix: '" +
pkg.$private.libraries[i].target.suffix + "'\n");
cfg = cfg.concat(tab + tab + "}\n");
cfg = cfg.concat(tab + "],\n");
}
cfg = cfg.concat("];\n");
// print("build.cfg = " + cfg);
/* if cfg file exists and is identical to cfg, don't overwrite it */
utils.saveFile(cfg, filename);
}
/*
* ======== mkProj ========
* For each target used in the package, generate the debugger files for
* the package.
*
* Params:
* pkg - the package object after the package build script
* completes
*/
function mkProj(pkg, out)
{
/* for every target used in this package */
var targs = pkg.$private.targets;
var flist = {};
for (var i in targs) {
var template;
var fPattern;
var targ = targs[i];
if (targ.debugGen != null) {
template = targ.debugGen.packageTemplate;
fPattern = targ.debugGen.packagePattern;
}
/* if there is a package-level file template, generate the file */
if (template != null && fPattern != null) {
/* generate the output file name */
var _bldUtils = xdc.useModule('xdc.bld.Utils');
var fname = _bldUtils.expandString(fPattern, {
trgSuffix: targ.suffix,
trgName: targ.name,
pkgName: pkg.name
});
/* if we haven't already generated this file */
if (flist[fname] == null) {
out.write("clean:: \n\t-$(RM) " + fname + "\n");
pkg.$private.generatedFiles[fname] = 1;
/* generate its contents */
var t = xdc.loadTemplate(template);
t.genFile(fname, pkg, [targ, pkg.$private.srcs]);
flist[fname] = fname;
}
}
}
}
/*
* ======== mkXML ========
*/
function mkXML(pkg, fileName)
{
var file = new java.io.File(fileName);
file["delete"]();
var encoding = java.nio.charset.Charset.defaultCharset().name();
var out = new java.io.BufferedWriter(new java.io.FileWriter(file));
out.write('<?xml version="1.0" encoding="' + encoding+ '"?>\n');
// out.write('<?xml-stylesheet type="text/xsl" href="build.xsl"?>\n');
out.write('<!-- This file conforms to the DTD xdc/bld/build.dtd -->\n');
out.write('<package name="' + pkg.name
+ '" version="' + pkg.version
+ '" producerId="' + escape(pkg.producerId) + '">\n');
var indent = " ";
/* output interfaces and modules */
out.write(indent + '<units>\n');
for (var i = 0; i < pkg.interfaces.length; i++) {
out.write(indent + ' <interface name="'
+ pkg.interfaces[i] + '"/>\n');
}
for (var i = 0; i < pkg.modules.length; i++) {
out.write(indent + ' <module name="'
+ pkg.modules[i] + '"/>\n');
}
out.write(indent + '</units>\n');
/* output all source files */
out.write(indent + '<sources>\n');
var srcs = pkg.$private.srcs;
for (var i in srcs) {
if (typeof srcs[i] != 'string') {
continue;
}
out.write(indent + ' <srcFile name="' + srcs[i] + '"/>\n');
}
out.write(indent + '</sources>\n');
/* output all configuration scripts */
out.write(indent + '<configscripts>\n');
srcs = pkg.$private.cfgs;
for (var i in srcs) {
out.write(indent + ' <srcFile name="' + srcs[i] + '"/>\n');
}
out.write(indent + '</configscripts>\n');
/* output all makefile files */
out.write(indent + '<makefiles>\n');
srcs = pkg.$private.makefiles;
for (var i = 0; i < srcs.length; i++) {
out.write(indent + ' <srcFile name="' + srcs[i].name
+ '" src="' + srcs[i].src + '"/>\n');
}
out.write(indent + '</makefiles>\n');
/* output all targets */
out.write(indent + '<targets>\n');
srcs = pkg.$private.targets;
var it3 = xdc.module("xdc.bld.ITarget3");
var sav = indent;
indent = indent.concat(' ');
for (var i in srcs) {
var targ = srcs[i];
var t3 = it3.Module(targ);
var rawVer = (t3 != null) ? t3.rawVersion : "";
out.write(indent + '<target name="' + targ.$name + '"\n '
+ indent + 'version="' + targ.version + '"\n '
+ indent + 'rawVersion="' + ("" + rawVer) + '"\n '
+ indent + 'platform="' + targ.platform + '"\n '
+ indent + 'isa="' + targ.isa + '"\n '
+ indent + 'suffix="' + targ.suffix + '"\n '
+ indent + 'rootDir="' + targ.rootDir + '"\n '
+ indent + 'os="' + targ.os + '"\n '
+ indent + 'rts="' + targ.rts + '">\n');
out.write(indent + ' <model endian="' + targ.model.endian + '"\n '
+ indent + ' codeModel="' + targ.model.codeModel + '"\n '
+ indent + ' dataModel="' + targ.model.dataModel + '"/>\n');
out.write(indent + '</target>\n');
}
indent = sav;
out.write(indent + '</targets>\n');
/* output all libraries */
out.write(indent + '<libraries>\n');
var libs = pkg.$private.libraries;
for (var i in libs) {
out.write(indent + '<library name="' + libs[i].name + '"\n '
+ indent + 'pname="' + libs[i].$private.name + '"\n '
+ indent + 'profile="' + libs[i].attrs.profile + '"\n '
+ indent + 'target="' + libs[i].target.$name + '"/>\n');
}
out.write(indent + '</libraries>\n');
/* output all configurations */
out.write(indent + '<configurations>\n');
var cfgs = pkg.$private.configurations;
for (var i in cfgs) {
out.write(indent + '<configuration name="' + cfgs[i].name + '"\n '
+ indent + 'pname="' + cfgs[i].$private.name + '"\n '
+ indent + 'target="' + cfgs[i].target.$name + '"\n '
+ indent + 'platform="' + cfgs[i].platform + '"\n '
+ indent + 'profile="' + cfgs[i].attrs.profile + '"\n '
+ indent + 'isPartial="' + cfgs[i].attrs.prelink + '"\n '
+ indent + 'xCfgScript="' + cfgs[i].$private.xCfgScript + '"\n '
+ indent + 'xCfgPrefix="' + cfgs[i].$private.xCfgPrefix + '"\n '
+ indent + 'cfgScript="' + cfgs[i].attrs.cfgScript + '"\n ');
if (cfgs[i].attrs.cfgArgs != null) {
out.write(indent + 'cfgArgs="' + escape(cfgs[i].attrs.cfgArgs) + '"\n ');
}
out.write(indent + '/>\n');
}
out.write(indent + '</configurations>\n');
/* output all executables */
out.write(indent + '<executables>\n');
var exes = pkg.$private.executables;
for (var i = 0; i < exes.length; i++) {
out.write(indent + '<executable name="' + exes[i].name + '"\n '
+ indent + 'pname="' + exes[i].$private.name + '"\n '
+ indent + 'target="' + exes[i].target.$name + '"\n '
+ indent + 'platform="' + exes[i].platform + '"\n '
+ indent + 'profile="' + exes[i].attrs.profile + '"\n '
+ indent + 'xCfgScript="' + exes[i].$private.xCfgScript + '"\n '
+ indent + 'xCfgPrefix="' + exes[i].$private.xCfgPrefix + '"\n '
+ indent + 'cfgScript="' + exes[i].attrs.cfgScript + '"\n ');
if (exes[i].attrs.cfgArgs != null) {
out.write(indent + 'cfgArgs="' + escape(exes[i].attrs.cfgArgs) + '"\n ');
}
out.write(indent + '/>\n');
}
out.write(indent + '</executables>\n');
/* output all exe tests */
out.write(indent + '<tests>\n');
for (var i = 0; i < exes.length; i++) {
var tests = exes[i].$private.tests;
for (var j = 0; j < tests.length; j++) {
var tattrs = tests[j].attrs;
out.write(indent + '<test exeName="' + exes[i].$private.name + '"\n '
+ indent + 'args="' + escape(tattrs.args) + '"\n '
+ indent + 'groupName="' + tattrs.groupName + '"\n '
+ indent + 'refOutput="' + escape(tattrs.refOutput) + '"\n '
+ indent + 'refExitCode="' + tattrs.refExitCode + '"\n '
+ indent + 'execArgs="' + escape(tattrs.execArgs) + '"\n ');
if (tattrs.execCmd != null) {
out.write(indent + 'execCmd="' + escape(tattrs.execCmd) + '"\n ');
}
out.write(indent + '/>\n');
}
}
/* output all script tests */
for each (var script in pkg.$private.scripts) {
var tests = script.$private.tests;
for (var j = 0; j < tests.length; j++) {
var tattrs = tests[j].attrs;
var execCmd;
var execArgs = tattrs.execArgs;
if (tattrs.execCmd != null) {
execCmd = tattrs.execCmd;
}
else if (script.name.match(/\.ksh$|\.sh$/) != null) {
execCmd = "$(SHELL) ";
}
else {
execCmd = "$(XSRUN) ";
execArgs += (script.attrs.hasMain ? " -c" : " -f");
}
out.write(indent + '<test exeName="' + script.name +'"\n ');
out.write(indent + 'args="' + escape(tattrs.args) + '"\n ');
out.write(indent + 'groupName="' + tattrs.groupName + '"\n ');
out.write(indent + 'refOutput="' + escape(tattrs.refOutput) + '"\n ');
out.write(indent + 'refExitCode="' + tattrs.refExitCode + '"\n ');
out.write(indent + 'execArgs="' + escape(execArgs) + '"\n ');
out.write(indent + 'execCmd="' + escape(execCmd) + '"\n ');
out.write(indent + '/>\n');
}
}
out.write(indent + '</tests>\n');
/* output all releases */
out.write(indent + '<releases relDir="' + pkg.relDir + '">\n');
for (var rname in pkg.$private.releases) {
var release = pkg.$private.releases[rname];
out.write(indent + '<release name="' + rname + '"\n '
+ indent + 'pname="'
+ release.attrs.prefix + release.$private.name + '"\n '
+ indent + 'label="' + release.attrs.label + '"\n ');
if (release.attrs.relScript != null) {
out.write(indent + 'relScript="' + release.attrs.relScript
+ '">\n');
}
else {
out.write('>\n');
}
for (var i in release.$private.files) {
out.write(indent + ' <file name="' + i + '"/>\n');
}
out.write(indent + '</release>\n');
}
out.write(indent + '</releases>\n');
/* output all repositories */
out.write(indent + '<repositories>\n');
for (var rname in pkg.$private.repositories) {
var repository = pkg.$private.repositories[rname];
out.write(indent + '<repository name="' + rname + '"\n '
+ indent + 'pname="'
+ repository.$private.name + '">\n');
for (var pname in repository.$private.packages) {
var tmp = repository.$private.packages[pname];
var relFile = xdc.getPackageBase(pname) + tmp.rname;
out.write(indent + ' <importedRelease\n');
out.write(indent + ' package="' + pname + '"\n');
out.write(indent + ' pname="' + tmp.rname + '"\n');
out.write(indent + ' label="' + tmp.rdesc.label + '">\n');
out.write(indent + ' </importedRelease>\n');
}
out.write(indent + '</repository>\n');
}
out.write(indent + '</repositories>\n');
out.write('</package>\n');
out.close();
}
/*
* ======== _genCfg ========
* Generate an argument free configuration script for a program; the script
* contains arguments specified by the build script (including the specified
* platform).
*
* By encapsulating all arguments into a single script that is run without
* any arguments, and by only modifying the file when its contents change,
* we can know when re-configuration must occur: re-run configuration
* iff the date of the script (or anything it includes) is later than the
* corresponding *.c.
*/
function _genCfg(acfg, cfgDir, config, pkg, noasm)
{
/* get profile-specific generation files */
var gentab = [];
var profileOpts = _getProfile(acfg.target, acfg.attrs);
if (profileOpts != null) {
for (var i = 0; i < profileOpts.filters.length; i++) {
var filter = profileOpts.filters[i].$private.filter;
if (filter != null) {
var tmp = filter.getGenTab(acfg, cfgDir, config);
gentab = gentab.concat(tmp);
}
}
}
/* generate the "base name" of the generated configuration files */
var base = (cfgDir + config).replace(/\/\/+/g, '/');
/* if cfg file exists and is identical to cfg, don't overwrite it */
var cfgFile = base + ".cfg";
var tplt = xdc.loadTemplate("xdc/bld/cfg.xdt");
tplt.genFile(cfgFile, acfg, [base, noasm, pkg, gentab]);
// print("gencfg: cfgFile = " + cfgFile
// + ", config name = " + acfg.$private.name);
return (cfgFile);
}
/*
* ======== _getProfile ========
*/
function _getProfile(targ, attrs)
{
var profile = attrs.profile == null ? "release" : attrs.profile;
var profileOpts = targ.profiles[profile];
if (profileOpts != null) {
for (var j = 0; j < profileOpts.filters.length; j++) {
var desc = profileOpts.filters[j];
if (desc.$private.filter == null) {
var Mod = xdc.useModule(desc.moduleName);
if (!(Mod instanceof xdc.om['xdc.bld.ITargetFilter'].Module)) {
throw new Error("Target '"
+ targ.$name + "'s '" + profile
+ "' profile filter[" + j
+ "] module (" + desc.moduleName
+ ") is not an xdc.bld.ITargetFilter");
}
desc.$private.filter = Mod.create(desc.params);
}
}
}
return (profileOpts);
}
/*
* ======== _getArchiveOpts ========
* Parameters:
* targ ITarget.Module
* attrs Library.Attrs
*
* Return:
* string archiver options
*/
function _getArchiveOpts(targ, attrs)
{
var result;
var profileOpts = _getProfile(targ, attrs);
if (profileOpts != null && profileOpts.archiveOpts != null) {
result = profileOpts.archiveOpts + " "
+ (attrs.aropts == null ? "" : attrs.aropts);
}
else {
result = attrs.aropts;
}
return (result);
}
/*
* ======== _getCompileOpts ========
* Parameters:
* targ ITarget.Module
* attrs Object.Attrs
*
* Return:
* xdc.bld.ITarget.CompileOptions
*/
function _getCompileOpts(targ, attrs)
{
var result;
var profileOpts = _getProfile(targ, attrs);
if (profileOpts != null && profileOpts.compileOpts != null) {
result = new xdc.om['xdc.bld.ITarget'].
CompileOptions(profileOpts.compileOpts);
/* cfgcopts also need to have 'profileOpts.compileOpts.copts', in
* case we are compiling the generated C config file, and the user
* requested to use 'cfgcopts'.
*/
var copts = "";
if (profileOpts.compileOpts.copts != undefined) {
copts = profileOpts.compileOpts.copts;
}
if (result.cfgcopts != undefined) {
result.cfgcopts += copts;
}
else {
result.cfgcopts = copts;
}
}
else {
result = new xdc.om['xdc.bld.ITarget'].CompileOptions;
}
/* add in goal-specific options (if they exist) */
for (var i in result) {
if (i in attrs) {
if (attrs[i] != null || result[i] != null) {
if (result[i] == null) {
result[i] = attrs[i];
}
else if (attrs[i] != null) {
result[i] = result[i].concat(" " + attrs[i]);
}
}
}
}
return (result);
}
/*
* ======== _getLinkOpts ========
* Parameters:
* targ ITarget.Module
* attrs Executable.Attrs
*
* Return:
* string linker options
*/
function _getLinkOpts(targ, attrs)
{
var result;
var profileOpts = _getProfile(targ, attrs);
if (profileOpts != null && profileOpts.linkOpts != null) {
result = profileOpts.linkOpts + " "
+ (attrs.lopts == null ? "" : attrs.lopts);
}
else {
result = attrs.lopts;
}
return (result);
}
/*
* ======== _inPkg ========
* Determine if the file, cfname, is in the package specified by
* pdir.
*
* Returns true iff cfname is a file name that appears in the package
* base directory pdir (i.e., pdir contains "package.xdc") and *not* in
* any nested package of pdir.
*
* Note: The file cfname does not need to exist; i.e., this function may
* return true even if the file does not exist.
*
* Parameters:
* pdir is the canonical path name of the package's base directory
* followed by java.io.File.separator
* cfname is the canonical path name of a file to check
*/
function _inPkg(pdir, cfname)
{
/* if the canonical path of the package base isn't a prefix of
* the canonical file name, the file can't be in the package
*/
if (cfname.indexOf(pdir) != 0) {
return (false);
}
/* if there is no directory separator in the file name beyond pdir/,
* then the file is in the package (and not in a nested package).
* This is a "quick" check to avoid the more expensive check below.
*/
if ((cfname.substr(pdir.length)).indexOf(java.io.File.separator) == -1) {
return (true);
}
/* use Scan.findPackageSpec() to check if file is in a nested package */
var pf = Packages.xdc.services.intern.cmd.Scan.findPackageSpec(cfname);
if (pf != null) {
pf = (new java.io.File(pf)).getCanonicalPath();
if (pf == (pdir + "package.xdc")) {
return (true);
}
}
return (false);
}
/*
* ======== _lsPkg ========
*/
function _lsPkg(root, exclude)
{
var dirArray = [];
var prefix = root == "." ? "" : (root + java.io.File.separator);
/*
* ======== myFilter ========
* Return true if a file is potentially a source file in the package
* containing dir
*/
function myFilter(dir, file)
{
file = String(file);
if (file == "package" || file[0] == '.') {
return (false);
}
var name = prefix + file;
if (exclude[name] != null
|| exclude[name.replace(/\\/g, "/")] != null) {
return (false);
}
file = new java.io.File(name);
if (file.isDirectory()) {
var tmp = new java.io.File(name
+ java.io.File.separator + "package.xdc");
if (!tmp.exists()) { /* if name is not a nested package */
dirArray[dirArray.length] = name;
}
return (false);
}
return (true);
}
/* create an object that implements java.io.FilenameFilter */
var filter = java.io.FilenameFilter({accept : myFilter});
/* create a java.io.File object */
var file = new java.io.File(root);
/* get matching files in root and construct path names for this list */
var result = [];
var list = file.list(filter);
if (list != null) {
result = new Array(list.length);
for (var i = 0; i < list.length; i++) {
result[i] = prefix + list[i];
}
}
/* recurse into any non-nested-package sub-directories found in root */
for (var i = 0; i < dirArray.length; i++) {
result = result.concat(_lsPkg(dirArray[i], exclude));
}
return (result);
}
/*
* ======== _mkBanner ========
*/
function _mkBanner(out)
{
out.write("#\n# Do not edit this file. This file is generated from \n");
out.write("# package.bld. Any modifications to this file will be \n");
out.write("# overwritten whenever makefiles are re-generated.\n#\n");
}
/*
* ======== _mkCopyGoals ========
*/
function _mkCopyGoals(pkg, out)
{
/* generate a pattern rule for all "copy" goals */
out.write("%,copy:\n\t@$(if $<,,$(MSG) don\\'t know how to build $*; exit 1)");
out.write("\n\t@$(MSG) cp $< $@\n\t$(RM) $@\n\t$(CP) $< $@\n");
/* create a new set of "top-level" goals for the goals in ogoals */
var ogoals = pkg.$private.copyGoals;
var ngoals = {};
for (var g in ogoals) {
var basename = g.substring(g.lastIndexOf('/') + 1);
var list = ngoals[basename];
if (list == null) {
ngoals[basename] = [g];
}
else {
/* more than one ogoal has the same basename */
ngoals[basename].push(g);
}
}
for (var g in ngoals) {
var oga = ngoals[g];
if (oga.length == 1) {
out.write(g + ",copy : " + oga[0] + "\n");
}
else {
out.write(".PHONY: " + g + ",copy\n");
out.write(g + ",copy : ");
for (var i = 0; i < oga.length; i++) {
out.write(g + "," + i + ",copy ");
}
out.write("\n\t@\n\n");
oga.sort();
for (var i = 0; i < oga.length; i++) {
out.write(g + "," + i + ",copy : " + oga[i] + "\n");
}
}
}
}
/*
* ======== _mkDirs ========
* Generate rules to automatically create all directories necessary
* for the build *and* the rules to remove the directories during a
* clean.
*/
function _mkDirs(pkg, out)
{
/* Create an array of all directories (including sub-directories) that
* need to be created.
*/
var dirSet = {};
var dirArr = [];
for (var dir in pkg.$private.dirs) {
var da = dir.split(/[\\\/]+/);
var tmp = "";
for (var i = 0; i < da.length; i++) {
if (da[i].length > 0 && da[i] != ".") {
tmp = tmp.concat(da[i] + "/");
if (dirSet[tmp] != 1) {
dirSet[tmp] = 1;
dirArr[dirArr.length] = tmp.substring(0, tmp.length - 1);
}
}
}
}
/* Generate rules to create the directories determined above.
*
* Note that the directories are not created when the make command is
* "clean". This is not just an optimization: GNU make seems to keep
* directories open when they appear in a "wildcard" expression.
* Since Win32 does not allow deletion of a file/directory while it
* is open, it is impossible to remove the directories with a clean
* command unless we don't reference the directories during a clean.
*/
out.write("ifneq (clean,$(MAKECMDGOALS))\n");
for (var i = 0; i < dirArr.length; i++) {
var dir = dirArr[i];
/* skip over directories like "../" */
if (dir.match(/^[\.\\\/]*$/) == null) {
out.write("ifeq (,$(wildcard " + dir + "))\n");
out.write(" $(shell $(MKDIR) " + dir + ")\n");
out.write("endif\n");
}
}
out.write("endif\n");
/* Generate rules to remove the "generated" directories */
out.write("clean::\n");
if (0) { /* don't remove all dirs; some may contain "precious" files */
for (var i = 0; i < dirArr.length; i++) {
/* skip over directories like "../" */
if (dir.match(/^[\.\\\/]*$/) == null) {
out.write("\t-$(RMDIR) " + dirArr[i] + "\n");
}
}
}
else {
/* remove package because this only contains generated files */
out.write("\t-$(RMDIR) package\n");
pkg.$private.generatedFiles["package"] = 1;
}
out.write("\n");
}
/*
* ======== _mkDefs ========
*/
function _mkDefs(target, file)
{
var PRE = "xdc_bld__";
/* required definitions */
var defs = "-Dxdc_target_name__=" + target.name;
defs += " -Dxdc_target_types__=" + target.stdInclude;
/* definitions from the build engine */
defs += " -D" + PRE + "profile_" + file.profile;
var vers = target.getVersion();
defs += " -D" + PRE + "vers_"
+ vers.slice(vers.search("{") + 1).replace(/[.,]/g, "_");
if (file.opts.defs == null) {
file.opts.defs = defs;
}
else {
file.opts.defs += (' ' + defs);
}
}
/*
* ======== _mkDll ========
* Like libraries, dll objects are placed in a sub-directory whose name
* matches the name of the dll. DLLs often have a "stub" library associated
* with them that allows client executables to link as though it were a static
* library. Since this stub library must have a different name from a static
* library containing the same objects, DLL directories must have names
* different from the corresponding static library directory.
*/
function _mkDll(pkg, dll, out)
{
}
/*
* ======== _mkDocs ========
*/
function _mkDocs(pkg, out)
{
var docDir = pkg.docDir;
if (docDir != null) {
out.write("all: .docs\n");
out.write(".docs: " + docDir + "/index.html\n");
out.write(docDir + "/index.html: .interfaces\n");
out.write("\t@$(MSG) generating docs in " + docDir + " ...\n");
out.write("\t$(XSRUN) xdc.tools.cdoc -s -od:" + docDir + "\n\n");
out.write("clean::\n");
out.write("\t$(RMDIR) " + docDir + "\n\n");
pkg.$private.generatedFiles[docDir] = 1;
/* We need to create 'docDir' early, with other directories,
* because the rule looking for 'docDir' may get processed before
* cdoc had a chance to create 'docDir'.
*/
getDir(docDir + "/index.html", pkg.$private.dirs);
}
}
/*
* ======== _mkExcludeList ========
*/
function _mkExcludeList(pkg, release, mname)
{
var ename = mname.replace(/\.inc$/, ".ninc");
var cout = "";
for (var i in release.$private.excludeDirs) {
cout = cout.concat(i + "\n");
}
utils.saveFile(cout, ename);
}
/*
* ======== _mkLib ========
* All library objects are placed in a sub-directory whose name matches
* the name of the library.
*
* Libraries are simply an "archive" of a collection of object files.
* Thus, libraries must be re-built iff
* 1. one of its objects needs to be re-built, or
* 2. the list of objects in the archive changes.
*
* We achive 1. by assuming that the object file dependencies are
* properly generated and adding a single dependency line containing all
* objects in the archive list.
*
* We achive 2 by placing a dependency on a <goal>.mak; the makefile
* generated that has all the rules for making the objects that go into the
* library. While this can cause unnecessary rebuilding of libraries, it
* ensures that if the list of objects to be placed in a library changes, it
* will be re-built.
*/
function _mkLib(pkg, lib, out)
{
debug("_mkLib " + lib.$private.name + " ...");
var i;
var prefix = pkg.libDir + lib.name + '/';
var goal = lib.$private.name;
var cmdSet = lib.target.suffix;
/* create a list of all objects that need to be built for this library */
var objs = {};
/* generate package.bld.c, if necessary */
var libAttrs = lib.attrs;
if (pkg.libTemplate != null) {
/* only generate package.bld.c once for all libraries */
if (pkg.$private.libTemplate == null) {
var tpath = xdc.findFile(pkg.libTemplate);
if (tpath != null) {
/* template changes should trigger re-gen of package.bld.c */
out.write("ifneq (clean,$(MAKECMDGOALS))\n");
out.write("package.mak:" + tpath + "\n");
/* gen empty rule to prevent make from failing when this file
* does not exist; make should simply regen package.mak with
* a possibly different tpath (that hopefully exists).
*/
out.write(tpath + ":\n\n");
out.write("endif\n");
}
pkg.$private.libTemplate = xdc.loadTemplate(pkg.libTemplate);
pkg.$private.libTemplate.genFile("package/package.bld.c",
pkg, [tpath]);
}
/* tell package/package.c to include package.bld.c */
libAttrs = new xdc.om['xdc.bld.Library'].Attrs(lib.attrs);
var defs = libAttrs.defs == null ? "" : libAttrs.defs;
/* package.c includes __xdc_bld_pkg_c__ (if it's defined) */
libAttrs.defs = defs
+ " -D__xdc_bld_pkg_c__=package.bld.c";
}
/* add generated package.c (which #includes the generated package.bld.c)
* to every library. This allows user's to embed version/meta data into
* the every library.
*/
objs["package/package_" + pkg.name] = {
noExtension: true,
srcPrefix: "",
srcSuffix: ".c",
dstPrefix: prefix,
attrs: libAttrs
};
/* add objects to the library */
for (var i in lib.$private.objects) {
objs[i] = {
dstPrefix: prefix, /* BUG?? */
attrs: lib.$private.objects[i].attrs
};
}
/* add library to ".library" goals so we can make just the libraries */
out.write(".libraries," + cmdSet + " .libraries: " + goal + "\n\n");
/* add library to the specified releases and compute srcRels */
var ra = lib.attrs.releases;
var srcRels = [];
for (var i = 0; i < ra.length; i++) {
var rel = ra[i];
rel.$private.files[goal] = goal;
if ((lib.attrs.exportSrc == null && rel.attrs.exportSrc == true)
|| lib.attrs.exportSrc == true) {
srcRels.push(rel);
}
}
/* object file rules are in a separate file to track compiler changes */
var makeFileName = goal + ".mak";
out.write("-include " + makeFileName + "\n");
/* generate rules for making the objects collected above */
var objList = _mkObjs(objs, lib.target, makeFileName, goal, pkg, srcRels);
/* output a list of object file names to be archived in the library */
var da = lib.name.split(/[\/\\]/);
var base = da.pop();
var archArgs = new xdc.om['xdc.bld.ITarget'].ArchiveGoal({
base: base,
dstPrefix: da.join('/') + '/',
dstSuffix: lib.attrs.suffix,
files: objList,
opts: _getArchiveOpts(lib.target, lib.attrs),
profile: lib.attrs.profile ? lib.attrs.profile : "release",
});
var res = lib.target.archive(archArgs);
if (res != null) {
var profObj = lib.target.profiles[
lib.attrs.profile ? lib.attrs.profile : "release"];
if (profObj != null) {
for (var i = 0; i < profObj.filters.length; i++) {
var filter = profObj.filters[i].$private.filter;
if (filter != null) {
filter.archive(goal, lib, objList, archArgs, res);
}
}
}
out.write(goal + ": \n");
out.write(_mkRule(res, goal, lib.target));
}
/* generate rules to clean generated goal */
out.write("clean," + cmdSet + " clean::\n\t-$(RM) " + goal + "\n");
}
/*
* ======== _mkManifest ========
* Create a file named mname that contains every file that is part of
* the release; one file per line. Only update the file if the contents
* differ from what already exists; this way, we won't unnecessarily
* re-run the archive command to create identical releases.
*
* Note: this manifest may contain other "*.inc" files that list
* additional files to include. We don't expand these files here because
* they don't exist until after a program is configured; the *.inc files
* are recursively expanded in the MKREL command.
*/
function _mkManifest(pkg, release, mname)
{
var nexp = 0;
var cout = "";
/* generate the exclude list */
_mkExcludeList(pkg, release, mname);
if (release.attrs.exportAll) {
var manifest;
if (pkg.$private.manifest == null) {
var exclude = pkg.$private.generatedFiles;
/* The files that should also be excluded are ones from
* Pkg.generatedFiles that are NOT in Pkg.otherFiles.
*/
for (var i = 0; i < Pkg.generatedFiles.length; i++) {
var foundInOther = false;
for (var j = 0; j < Pkg.otherFiles; j++) {
if (Pkg.generatedFiles[i] == Pkg.otherFiles[j]) {
foundInOther = true;
break;
}
}
if (!foundInOther) {
exclude[Pkg.generatedFiles[i]] = 1;
}
}
manifest = _lsPkg(".", exclude);
pkg.$private.manifest = manifest;
}
else {
manifest = pkg.$private.manifest;
}
nexp = manifest.length;
if (nexp > 0) {
cout = manifest.join("\n") + "\n";
}
}
if ((release.attrs.exportDoc || release.attrs.exportAll)
&& pkg.docDir != null
&& !(new java.io.File(pkg.docDir)).isAbsolute()) {
release.$private.files[pkg.docDir] = pkg.docDir;
}
for (var i in release.$private.files) {
nexp++;
cout = cout.concat(i + "\n");
}
/* verbatim files are only used by release scripts to determine
* directories to "skip" over in filtering; i.e., they should be taken
* "verbatim".
*/
if (release.attrs.relScript) {
_mkVerbatim(pkg, release, mname + ".verbatim");
}
if (nexp > 0) {
utils.saveFile(cout, mname);
}
return (nexp);
}
/*
* ======== _mkVerbatim ========
* Rather than look at release.$private.files we explicitly add
* known directories since the list of files will contain lots of
* non-existant files and directories.
*/
function _mkVerbatim(pkg, release, fileName)
{
var list = [];
for each (var r in pkg.$private.repositories) {
list.push(r.name);
}
if (pkg.docDir != null) {
list.push(pkg.docDir);
}
var olist = [];
for (var i = 0; i < pkg.otherFiles.length; i++) {
olist.push(pkg.otherFiles[i]);
}
for (var i = 0; i < release.otherFiles.length; i++) {
olist.push(release.otherFiles[i]);
}
for each (var i in olist) {
var lastChar = i[i.length - 1];
if (lastChar == '/' || lastChar == '\\'
|| java.io.File(i).isDirectory()) {
list.push(i);
}
}
if (list.length > 0) {
// print("verbatim files: " + list);
var file = new java.io.File(fileName);
file["delete"]();
var out = new java.io.BufferedWriter(new java.io.FileWriter(file));
for each (var i in list) {
out.write(i + '\n');
}
out.close();
}
}
/*
* ======== _mkObjs ========
* For each object in objs we generate the rule to make the object.
*
* The objs object is a hashtable indexed by the "basename" name of the
* object to create (no extension). Each entry has the following
* properties:
* dstPrefix - required directory for the output object file
* attrs - required non-null object with optional properties:
* defs
* copts
* srcSuffix - (optional string) if defined and matches known
* source file, the source file does not need to exist
* (it is assumed to be generated from another source).
* If this property is not defined, the source file must
* exist and must have a known suffix (see rules below).
* srcPrefix - (optional string) if defined and source file does
* not exist, this string is the directory containing
* the source file.
*
* Objects must be re-built if build options change. We ensure this by
* placing a dependency on makeFileName. This will result in some
* unnecessary rebuilding but will never omit a necessary rebuild.
*
* The generated .xdc.h file(s) used by a source file must exist before
* compiling objects in the package. This is ensured by making ".interfaces"
* a "order-only" prerequisite of all objects.
*/
function _mkObjs(objs, target, makeFileName, objListGoal, pkg, srcRels, cfgBase)
{
debug("_mkObjs(..., " + makeFileName + ", " + objListGoal + ", ...)");
var srcs = pkg.$private.srcs;
var deps = pkg.$private.makefiles;
var copies = pkg.$private.copyGoals;
var asmList = [];
var objList = [];
var srcList = [];
var objString = "";
function findFile(extensions, goal) {
/* if we're given the name of the source, assume it exists (or will) */
if (goal.srcSuffix != null && goal.srcPrefix != null) {
return (goal);
}
if (goal.srcSuffix == null) {
goal.srcSuffix = "";
}
if (goal.srcPrefix == null) {
goal.srcPrefix = "";
}
/* if a file extension is supplied, parse the name cannonically */
var srcFile = goal.srcPrefix + goal.base + goal.srcSuffix;
var dotIndex = srcFile.lastIndexOf(".");
if (dotIndex > 0 && srcFile.substring(dotIndex).indexOf("/") == -1) {
/* source file with a specific extension has been specified */
goal.srcSuffix = srcFile.substring(dotIndex);
/* BUG??? Check for file existance ??? */
var dstFile = goal.dstPrefix + goal.base;
var slashIndex = dstFile.lastIndexOf("/") + 1;
goal.dstPrefix = dstFile.substring(0, slashIndex);
slashIndex = srcFile.lastIndexOf("/") + 1;
goal.srcPrefix = srcFile.substring(0, slashIndex);
goal.base = goal.base.substring(slashIndex, dotIndex);
return (goal);
}
/* otherwise look for file using the target's extensions table */
for (var i = 0; i < extensions.length; i++) {
srcFile = goal.srcPrefix + goal.base + extensions[i].suf;
var file = new java.io.File(srcFile);
if (file.exists()) {
goal.srcSuffix = extensions[i].suf;
return (goal);
}
}
/* otherwise, fail */
return (null);
}
deps[deps.length++] = {name: makeFileName, src: "package.bld"};
/* create a byte stream to contain this collection's objs rules */
var byteStream = new java.io.ByteArrayOutputStream(1024);
var out = new java.io.OutputStreamWriter(byteStream);
/* generate a banner that changes whenever a different compiler is used;
* this ensures that a change to the compiler forces a rebuild (even if
* the path to the compiler is the same.)
*/
_mkBanner(out);
out.write("# target compatibility key = " + target.version + "\n#\n");
/* generate rules for each object */
for (var name in objs) {
var obj = objs[name];
debug(" make obj " + name + " ...");
/* skip over pre-built files */
if ("preBuilt" in obj.attrs && obj.attrs.preBuilt == true) {
objString = objString.concat(name + " ");
continue;
}
var dstSuffix = ".o" + target.suffix;
var goalFile = obj.dstPrefix + name + dstSuffix;
if (obj.noExtension == null || obj.noExtension != true) {
var dotIndex = name.lastIndexOf(".");
if (dotIndex > 0 && name.substring(dotIndex).indexOf("/") == -1) {
goalFile = obj.dstPrefix + name.substr(0, dotIndex) + dstSuffix;
}
}
goalFile = goalFile.replace(/\/\/+/g, '/');
var file = findFile(target.extensions, {
base : name,
dstPrefix: obj.dstPrefix,
dstSuffix: dstSuffix,
srcPrefix: obj.srcPrefix,
srcSuffix: obj.srcSuffix,
});
var res = null;
var compGoal = null;
var srcFile;
if (file != null) {
srcFile = file.srcPrefix + file.base + file.srcSuffix;
srcFile = srcFile.replace(/\/\/+/g, '/');
file.opts = _getCompileOpts(target, obj.attrs);
file.profile = obj.attrs.profile ? obj.attrs.profile : "release";
compGoal = new xdc.om['xdc.bld.ITarget'].CompileGoal(file);
_mkDefs(target, compGoal);
compGoal.configOpts = false;
if (obj.configOpts != undefined) {
compGoal.configOpts = obj.configOpts;
}
res = target.compile(compGoal);
}
if (res != null) {
srcList[srcList.length] = srcFile;
objList[objList.length] = goalFile;
var dep = goalFile + ".dep";
deps[deps.length++] = {name: dep, src: srcFile};
//out.write("ifneq (clean,$(MAKECMDGOALS))\n");
out.write("ifeq (,$(MK_NOGENDEPS))\n");
out.write("-include " + dep + "\n");
out.write(dep + ": ;\n");
out.write("endif\n\n");
var cfgHeader = cfgBase != null ? (cfgBase + ".h") : "";
out.write(goalFile + ": | .interfaces\n");
out.write(goalFile + ": " + srcFile + " " + makeFileName + " " + cfgHeader + "\n");
out.write("\t@$(RM) $@.dep\n");
/*
* Fixup generated dependencies by replacing all '\'s with
* '/' this is necessary because make does not recognize
* "foo/bar.c" and "foo\bar.c" as identical goals.
*
* To simply convert slashes, the following sed command can
* be used:
* '@$(SED) -e "s:\\\\\\\\:/:g" $@.dep > $@.tmp\n';
*
* We use custom FIXDEP so that we can also generate empty
* rules for files not in the current package (and avoid
* starting up a shell just to do the redirection).
*/
res.cmds += '\n-@$(FIXDEP) $@.dep $@.dep\n';
var profile = target.profiles[file.profile];
if (profile != null) {
for (var i = 0; i < profile.filters.length; i++) {
var filter = profile.filters[i].$private.filter;
if (filter != null) {
filter.compile(objListGoal, target, goalFile, file, res);
}
}
}
out.write(_mkRule(res, goalFile, target));
srcs[srcFile] = srcFile;
copies[goalFile] = goalFile;
/* create rule to make asm source rather than object file */
file.dstSuffix = ".s" + target.suffix;
compGoal = new xdc.om['xdc.bld.ITarget'].CompileGoal(file);
_mkDefs(target, compGoal);
if ((res = target.scompile(compGoal)) != null) {
goalFile = goalFile.replace(
new RegExp(dstSuffix + '$'), file.dstSuffix);
copies[goalFile] = goalFile;
out.write(goalFile + ": | .interfaces\n");
out.write(goalFile + ": " + srcFile + " "
+ makeFileName + "\n");
out.write("\t@$(RM) $@.dep\n");
res.cmds += '\n-@$(FIXDEP) $@.dep $@.dep\n';
out.write(_mkRule(res, goalFile, target));
asmList[asmList.length] = goalFile;
}
}
else {
java.lang.System.err.print("Warning: target "
+ target.$name
+ " doesn't know how to create '" + goalFile
+ (file != null ?
"' from '" + srcFile + "'\n" :
("'; can't find source file for '" + name + "'\n")));
}
}
if (objList.length > 0) {
/* add source files to specified releases */
for (var i = 0; i < srcRels.length; i++) {
var rel = srcRels[i];
for (var j = 0; j < srcList.length; j++) {
rel.$private.files[srcList[j]] = srcList[j];
}
}
/* add rules to remove generatable object and asm source files */
out.write("clean," + target.suffix + " ::\n");
for (var i = 0; i < objList.length; i++) {
var objfile = objList[i];
pkg.$private.generatedFiles[objfile] = 1;
out.write("\t-$(RM) " + objfile + "\n");
getDir(objList[i], pkg.$private.dirs);
objString = objString.concat(objfile + " ");
}
for (var i = 0; i < asmList.length; i++) {
out.write("\t-$(RM) " + asmList[i] + "\n");
}
out.write("\n");
}
/* if the object list changes or makefile changes, remake goal */
out.write(objListGoal + ": " + objString + makeFileName + "\n");
/* clean removes this makefile, but don't remove it on target-specific
* clean; otherwise we need to re-make the makefile!
*/
out.write("\nclean::\n\t-$(RM) " + makeFileName + "\n");
pkg.$private.generatedFiles[makeFileName] = 1;
out.flush();
/* write byte stream to the specified makefile if the contents differ */
utils.saveFile(byteStream.toString(), makeFileName);
out.close();
byteStream.close();
return (objString);
}
/*
* ======== _mkProg ========
* Executables are configurations of a module and the specification of a
* "main" entry point. Thus,
* 1. a program's configuration must be re-run if its configuration
* script, its arguments, any included script, or any included
* interface change.
*
* 2. a program must be relinked if any library it requires changes,
* or the set of libraries changes
*
* We achieve 1. by generating a configuration script, <cfg>.cfg, that
* encapsulates the program's configuration script and arguments. We then
* make the program's <cfg>.c file depend on this generated script. In
* addition, when <cfg>.cfg runs it creates a dependency file, <cfg>.dep,
* that adds prerquisites to <cfg>.c of <pkg>/.interfaces and any scripts
* loaded by <cfg>.cfg. The dependency file <cfg>.dep is included if it
* exists.
*
* We achive 2. by generating explicit library dependencies when the
* program's configuration script is run (see <cfg>.dep). In addition, we
* add the prerequisite <cfg>.xdl to the executable. The set of libraries
* to link with can only change if a configuration script changes. But if
* one of these scripts changes we will re-configure and if the library set
* changes, <cfg>.xdl will change.
*
* TODO: In order to detect configuration interface changes we rely on
* the date of the .interfaces file of each imported package. This is
* overly conservative since the interfaces may not change but the
* .interfaces file may; e.g., clean and rebuild a package and any
* program that imports the package will re-configure when you build
* the program's package.
*/
function _mkProg(pkg, prog, out)
{
debug("_mkProg " + prog.$private.name + " ...");
var goal = prog.$private.name;
var cmdSet = prog.target.suffix;
var config = prog.$private.name.replace(/\.([^\.]*$)/, "_$1");
var cfgDir = pkg.cfgDir;
var cfgBase = cfgDir + config;
cfgBase = cfgBase.replace(/\/\/+/g, '/'); /* remove double '/'s; see Bug 456564 */
/* make sure it is built when .executables is the goal */
var g = prog.$private.noasm ? ".executables" : ".dlls";
out.write(g + "," + cmdSet + " " + g + ": " + goal + "\n\n");
/* add program to the specified releases */
var ra = prog.attrs.releases;
var srcRels = [];
for (var i = 0; i < ra.length; i++) {
var rel = ra[i];
if ((prog.attrs.exportExe == null && rel.attrs.exportExe == true)
|| prog.attrs.exportExe == true) {
rel.$private.files[goal] = goal;
}
if ((prog.attrs.exportCfg == null && rel.attrs.exportCfg == true)
|| prog.attrs.exportCfg == true) {
rel.$private.files[cfgBase + ".xdc.inc"] = cfgBase + ".xdc.inc";
rel.$private.files[cfgBase + ".cfg"] = cfgBase + ".cfg";
}
if ((prog.attrs.exportSrc == null && rel.attrs.exportSrc == true)
|| prog.attrs.exportSrc == true) {
srcRels.push(rel);
}
}
/* create a list of all objects that need to be built for this program */
var objs = {};
/* add XDC objects */
objs[config] = {
noExtension: true,
configOpts: true,
srcSuffix: ".c",
srcPrefix: cfgDir,
dstPrefix: cfgDir,
attrs: prog.attrs.$copy()
};
for (var i in prog.$private.objects) {
var dst = cfgDir + prog.name + "/";
objs[i] = {
dstPrefix: dst,
attrs: prog.$private.objects[i].attrs.$copy(),
};
}
/* add -Dxdc_cfg__xheader__=... to all objects added to executable */
for (var i in objs) {
var attrs = objs[i].attrs;
if (attrs.defs == null) {
attrs.defs = "";
}
attrs.defs += " -Dxdc_cfg__xheader__='\""
+ pkg.name.replace(/\./g, '/')
+ "/" + cfgBase + ".h\"' ";
}
/* generate rules to compile all added objects */
var makeFileName = cfgBase + ".mak";
out.write("-include " + makeFileName + "\n");
var objList = _mkObjs(objs, prog.target, makeFileName, goal, pkg, srcRels);
/* generate an executable-specific configuration script */
var cfgFile = _genCfg(prog, cfgDir, config, pkg, prog.$private.noasm);
prog.$private.xCfgScript = cfgFile;
prog.$private.xCfgPrefix = cfgDir + config;
/* include generated dependencies from running .cfg script */
var dep = cfgBase + ".dep";
pkg.$private.makefiles[pkg.$private.makefiles.length++] = {
name: dep,
src : cfgFile
};
// out.write("ifneq (clean,$(MAKECMDGOALS))\n");
out.write("ifeq (,$(MK_NOGENDEPS))\n");
out.write("-include " + dep + "\n");
out.write(dep + ": ;\n");
out.write("endif\n");
/* create rule to link the program executable */
/* make sure that all objects are built *after* the config step; this
* allows these source file to include application-specific generated
* header (e.g., declarations of created objects)
*/
out.write(objList + ": | " + cfgBase + ".xdl\n");
var linkArgs = new xdc.om['xdc.bld.ITarget'].LinkGoal({
base: prog.name,
dstPrefix: "./",
dstSuffix: prog.$private.ext,
files: objList + " " + cfgBase + ".xdl",
opts: _getLinkOpts(prog.target, prog.attrs),
profile: prog.attrs.profile ? prog.attrs.profile : "release",
dllMode: !prog.$private.noasm,
});
var res = prog.target.link(linkArgs);
if (res != null) {
var profile = prog.target.profiles[
prog.attrs.profile ? prog.attrs.profile : "release"
];
if (profile != null) {
for (var i = 0; i < profile.filters.length; i++) {
var filter = profile.filters[i].$private.filter;
if (filter != null) {
filter.link(goal, cfgBase, prog, objList, linkArgs, res);
}
}
}
out.write(goal + ":\n");
out.write(_mkRule(res, goal, prog.target));
}
/*
* Changes to libraries SHOULD NOT trigger re-configuration BUT
* libraries must exist prior to running configuration; otherwise
* the configuration will fail (it insists that the libraries exist).
* Perhaps it is better to remove this check from configuration?
*
* Changes to libraries DO require re-link but this is covered by
* explicit dependencies generated by running the configuration
* script. The dependency for goal is here to allow one to specify
* goal as the build goal in a clean directory; without this
* dependency make does not know that it must first make libraries
* before making goal.
*/
out.write("\nifeq (,$(wildcard .libraries," + cmdSet + "))\n");
out.write(goal + " " + cfgBase + ".c: .libraries," + cmdSet + "\n");
out.write("endif\n\n");
/* create rules to generate configuration files */
out.write(cfgBase + ".c " + cfgBase + ".h "
+ cfgBase + ".xdl: override _PROG_NAME := "
+ prog.$private.name + "\n");
if (prog.attrs.xsopts != null) {
out.write(cfgBase + ".c "
+ cfgBase + ".xdl: override _PROG_XSOPTS = "
+ prog.attrs.xsopts + "\n");
}
out.write(cfgBase + ".c: " + cfgBase + ".cfg\n");
/* ensure config is re-run if a config interface changes */
out.write(cfgBase + ".xdl " + cfgBase + ".c: .interfaces\n");
/* create rules to run all tests */
if (prog.$private.noasm) {
_mkTests(prog, out);
}
/*
* create rules to clean generated goals
*
* target-specific clean does not remove the generated cfg or dep file;
* otherwise, we would have to remake the makefile which can be very
* expensive for packages with lots of files or executables.
*/
out.write("\nclean:: clean," + cmdSet + "\n");
out.write("\t-$(RM) " + cfgBase + ".cfg\n");
out.write("\t-$(RM) " + cfgBase + ".dep\n");
out.write("\t-$(RM) " + cfgBase + ".c\n");
out.write("\t-$(RM) " + cfgBase + ".xdc.inc\n");
out.write("\nclean," + cmdSet + "::\n");
out.write("\t-$(RM) " + goal + "\n");
pkg.$private.generatedFiles[cfgBase + ".cfg"] = 1;
pkg.$private.generatedFiles[cfgBase + ".dep"] = 1;
pkg.$private.generatedFiles[cfgBase + ".c"] = 1;
pkg.$private.generatedFiles[cfgBase + ".xdc.inc"] = 1;
if (prog.target.debugGen.execTemplate != null) {
var _bldUtils = xdc.useModule('xdc.bld.Utils');
var fname = _bldUtils.expandDbgName(cfgBase , goal,
prog.target.debugGen.execPattern);
out.write("clean:: \n\t-$(RM) " + fname + "\n");
pkg.$private.generatedFiles[fname] = 1;
}
}
/*
* ======== _mkProg2 ========
*/
function _mkProg2(pkg, aexe, out)
{
var acfg = aexe.$private.acfg;
/* legacy assembly/config support */
if (aexe.$private.noasm == false) {
_mkProg(pkg, aexe, out);
return;
}
if (acfg == null) {
print("assertion violation: cfg for '" + aexe.name
+ "' must exist prior to generating executables");
}
var extra = {
xCfgScript: acfg.$private.xCfgScript,
xCfgPrefix: acfg.$private.xCfgPrefix,
pkgName: pkg.name,
isPartial: acfg.attrs.prelink,
objList: acfg.$private.objList,
pname: acfg.$private.name,
};
_mkExe(pkg, aexe, extra, out);
}
/*
* ======== _mkExe ========
* We generate the following files, where tmpName = "package/cfg/<progName>"
* bld:
* tmpName.<ext>.mak rules to compile exe source
*
* cc:
* tmpName/<src>.o<suffix> source output object file
* tmpName/<src>.o<suffix>.dep header dependencies for object file
*
* lnk:
* <progName>.<ext> linked executable
* tmpName.<ext>.map linker output map file
*/
function _mkExe(pkg, aexe, sharedCfg, out)
{
debug("_mkExe " + aexe.$private.name + " ...");
var exeDir = pkg.cfgDir + aexe.name + "/";
/* record the executable's generated configuration script */
aexe.$private.xCfgScript = sharedCfg.xCfgScript;
aexe.$private.xCfgPrefix = sharedCfg.xCfgPrefix;
aexe.$private.xCfgPackage = sharedCfg.pkgName;
var cfgBase = sharedCfg.xCfgPrefix;
var goal = aexe.$private.name;
var cmdSet = aexe.target.suffix;
/* make sure it is built when .executables is the goal */
out.write(".executables," + cmdSet + " .executables: " + goal + "\n\n");
/* make exe depend on configuration's "physical" name */
if (aexe.attrs.sharedCfg != null || sharedCfg.isPartial) {
/* TODO: we should fix configs so the dependency below is always
* required. For the moment we don't create a pname file when
* the config is implicitly created, so we need to avoid creating
* the dependency below since this would cause exes with
* implicitly created configs to be re-linked on every build.
*/
out.write(goal + ": " + sharedCfg.pname + "\n\n");
}
else {
/* creating an order-only dependency works around the "relink" issue */
out.write(goal + ": |" + sharedCfg.pname + "\n\n");
}
/* add executable to the specified releases */
var ra = aexe.attrs.releases;
var srcRels = [];
for (var i = 0; i < ra.length; i++) {
var rel = ra[i];
if ((aexe.attrs.exportExe == null && rel.attrs.exportExe == true)
|| aexe.attrs.exportExe == true) {
rel.$private.files[goal] = goal;
}
if ((aexe.attrs.exportSrc == null && rel.attrs.exportSrc == true)
|| aexe.attrs.exportSrc == true) {
srcRels.push(rel);
}
}
/* create a list of all objects that must be built for this executable */
var objs = {};
for (var i in aexe.$private.objects) {
var attrs = aexe.$private.objects[i].attrs.$copy();
if (attrs.defs == null) {
attrs.defs = "";
}
/* add -Dxdc_cfg__header__=... to all objects added to executable */
attrs.defs += " -Dxdc_cfg__xheader__='\""
+ sharedCfg.pkgName.replace(/\./g,'/') + "/" + cfgBase + ".h\"' ";
objs[i] = {
dstPrefix: exeDir,
attrs: attrs
};
}
/* generate rules to compile the added objects */
var makeFileName = (pkg.cfgDir + aexe.$private.name + ".mak").replace(/\/\/+/g, '/');
out.write("-include " + makeFileName + "\n");
var objList = _mkObjs(objs, aexe.target, makeFileName, goal, pkg, srcRels, cfgBase);
/* create rule to link the program executable */
if (sharedCfg.pkgName != pkg.name) {
/* if cfg is in a different pkg we must use an absolute path */
cfgBase = xdc.getPackagebase(sharedCfg.pkgName) + cfgBase;
}
/* TODO: partial attribute of cfgs shouldn't change the linker cmd name */
var cmdFile = cfgBase + (sharedCfg.isPartial ? "_x.xdl" : ".xdl");
var linkArgs = new xdc.om['xdc.bld.ITarget'].LinkGoal({
base: aexe.name,
dstPrefix: "./",
dstSuffix: aexe.$private.ext,
files: sharedCfg.objList + " " + objList + " " + cmdFile,
opts: _getLinkOpts(aexe.target, aexe.attrs),
profile: aexe.attrs.profile ? aexe.attrs.profile : "release",
});
var res = aexe.target.link(linkArgs);
if (res != null) {
var profile = aexe.target.profiles[
aexe.attrs.profile ? aexe.attrs.profile : "release"
];
if (profile != null) {
for (var i = 0; i < profile.filters.length; i++) {
var filter = profile.filters[i].$private.filter;
if (filter != null) {
filter.link(goal, cfgBase, aexe, objList, linkArgs, res);
}
}
}
/* 'isPartial' set to 'true' means that 'prelink' is set to 'true'.
* If that's the case, objList contains .o<suffix> file. If 'prelink'
* is 'false', objList is empty, and .o<suffix> file is in
* sharedCfg.objList. The following code ensures that we always use
* .o<suffix> file as a prerequisite for the app.
*/
if (sharedCfg.isPartial) {
out.write(goal + ": " + objList + "\n");
}
else {
out.write(goal + ": " + sharedCfg.objList + "\n");
}
out.write(_mkRule(res, goal, aexe.target));
}
/* create rules to run all tests */
_mkTests(aexe, out);
/*
* create rules to clean generated goals
*
* target-specific clean does not remove the generated cfg or dep file;
* otherwise, we would have to remake the makefile which can be very
* expensive for packages with lots of files or executables.
*/
out.write("\nclean:: clean," + cmdSet + "\n");
out.write("\nclean," + cmdSet + "::\n");
out.write("\t-$(RM) " + goal + "\n");
if (aexe.target.debugGen.execTemplate != null) {
var _bldUtils = xdc.useModule('xdc.bld.Utils');
/* TODO: handle cfgBase in other package */
var fname = _bldUtils.expandDbgName(cfgBase, goal,
aexe.target.debugGen.execPattern);
out.write("clean:: \n\t-$(RM) " + fname + "\n");
pkg.$private.generatedFiles[fname] = 1;
}
}
/*
* ======== _mkConfig ========
* We generate the following files, where asmName = "package/cfg/<cfgName>"
* bld:
* asmName_p<suffix>.cfg target-platform binding script
* asmName_p<suffix>.mak rules to compile cfg generated sources
*
* cfg:
* asmName_p<suffix>.xdc.inc include files to add to a release;
* generated as side effect of xdc.cfg, see
* cfg.xdt + utils.genDep
* asmName_p<suffix>.xdl config linker command file; see Prog.java
* asmName_p<suffix>_x.xdl app linker command file; see xdc.cfg.Main
* asmName_p<suffix>.dep dependencies discovered during config
* goal: libs ...
* asmName_p<suffix>.c generated C data/code
* asmName_p<suffix>.h global declarartions of generated data
* asmName_p<suffix>.rta.xml RTA meta-data
* asmName_p<suffix>.rov.xs ROV config model recap
* asmName_p<suffix>.cfg.xml config model state (unused?)
* asmName_p<suffix>.cfg.dot config DOT graph (unused?)
*
* cc:
* asmName_p<suffix>.o<suffix> output object file
* asmName_p<suffix>.o<suffix>.dep header dependencies for object file
*
* lnk: (only if partial link is specified)
* asmName.p<suffix>.obj prelinked object
* asmName.p<suffix>.map prelinked map file
* goal physical name of the configuration
*
* make: (only if partial link is not specified)
* goal physical name of the configuration
*
* The build works roughly as follows, where goal is the "physical name" of
* the configuration and cfgBase is the prefix for the config generated files
* (asmName_p<suffix>):
*
* all: .dlls
* .dlls: goal
*
* goal: cfgBase.o<suffix>
* $(TOUCH) $@
*
* cfgBase.o<suffix>: cfgBase.c
* cfgBase.c: _PROG_NAME = goal
* $(CONFIG) cfgBase.cfg
* -include cfgBase.dep
*
* cfgBase.dep contains
* _PROG_NAME: pkg/foo.lib ...
*
* TODO: all configurations need to define _PROG_NAME; this name is in
* the generated .c file and is used as the goal in the asmName_p<suffix>.dep
* dependencies. But for non-partial linked configurations, the dependencies
* should trigger a re-link of _all_ applications that share the config.
* Perhaps we should _always_ generate the .p<suffix>.obj file, make this a
* pre-requisite of all executables using this config, and always use it as
* the goal.
*
*/
function _mkConfig(pkg, acfg, out)
{
debug("_mkConfig " + acfg.$private.name + " ...");
if (acfg.$private.noasm == false) {
return; /* nothing to do: generation will be handled by old mkProg() */
}
var isPartial = acfg.attrs.prelink ? true : false;
var config = acfg.$private.name.replace(/\.([^\.]*$)/, "_$1");
var cfgDir = pkg.cfgDir;
var cfgBase = cfgDir + config;
cfgBase = cfgBase.replace(/\/\/+/g, '/'); /* remove double '/'s; see Bug 456564 */
var goal = acfg.$private.name;
var cmdSet = acfg.target.suffix;
/* make sure it is built when .dlls is the goal */
out.write(".dlls," + cmdSet + " .dlls: " + goal + "\n\n");
/* add configuration to the specified releases */
var ra = acfg.attrs.releases;
var srcRels = [];
for (var i = 0; i < ra.length; i++) {
var rel = ra[i];
if ((acfg.attrs.exportCfg == null && rel.attrs.exportCfg == true)
|| acfg.attrs.exportCfg == true) {
rel.$private.files[cfgBase + ".xdc.inc"] = cfgBase + ".xdc.inc";
rel.$private.files[cfgBase + ".cfg"] = cfgBase + ".cfg";
if (isPartial == true) {
rel.$private.files[goal] = goal;
}
}
if ((acfg.attrs.exportSrc == null && rel.attrs.exportSrc == true)
|| acfg.attrs.exportSrc == true) {
srcRels.push(rel);
}
}
/* create a list of all objects that need to be built for this program */
var objs = {};
/* add -Dxdc_cfg__xheader__=... to compiled cfg object */
var attrs = acfg.attrs.$copy();
if (attrs.defs == null) {
attrs.defs = "";
}
attrs.defs += " -Dxdc_cfg__xheader__='\""
+ pkg.name.replace(/\./g, '/')
+ "/" + cfgBase + ".h\"' ";
objs[config] = {
noExtension: true,
configOpts: true,
srcSuffix: ".c",
srcPrefix: cfgDir,
dstPrefix: cfgDir,
attrs: attrs
};
/* generate rules to compile generated files */
var makeFileName = cfgBase + ".mak";
out.write("-include " + makeFileName + "\n");
var objList = _mkObjs(objs, acfg.target, makeFileName, goal, pkg, srcRels);
acfg.$private.objList = objList;
/* include xdc.cfg.SourceDir generated makefile (if it exists) */
out.write("-include " + cfgBase + ".cfg.mak\n");
/* generate the executable-specific configuration script */
var cfgFile = _genCfg(acfg, cfgDir, config, pkg, !isPartial);
acfg.$private.xCfgScript = cfgFile;
acfg.$private.xCfgPrefix = cfgBase;
/* include generated dependencies from running .cfg script */
var dep = cfgBase + ".dep";
pkg.$private.makefiles[pkg.$private.makefiles.length++] = {
name: dep,
src : cfgFile
};
// out.write("ifneq (clean,$(MAKECMDGOALS))\n");
out.write("ifeq (,$(MK_NOGENDEPS))\n");
out.write("-include " + dep + "\n");
out.write("endif\n");
/* if necessary, do the partial link */
if (isPartial) {
/* TODO: target should not control the output name!! */
acfg.$private.objList = cfgDir + acfg.$private.name + ".obj";
var linkArgs = new xdc.om['xdc.bld.ITarget'].LinkGoal({
base: acfg.name,
dstPrefix: "./", /* TODO: ignored by TI targets!!! */
dstSuffix: acfg.$private.ext,
files: objList + " " + cfgBase + ".xdl",
opts: _getLinkOpts(acfg.target, acfg.attrs),
profile: acfg.attrs.profile ? acfg.attrs.profile : "release",
dllMode: true,
});
var res = acfg.target.link(linkArgs);
if (res != null) {
var profile = acfg.target.profiles[
acfg.attrs.profile ? acfg.attrs.profile : "release"
];
if (profile != null) {
for (var i = 0; i < profile.filters.length; i++) {
var filter = profile.filters[i].$private.filter;
if (filter != null) {
/* TODO: how to pass exeBase to filters? */
filter.link(goal, cfgBase, acfg, objList, linkArgs, res);
}
}
}
out.write(goal + ":\n");
out.write(_mkRule(res, goal, acfg.target));
}
}
else {
if (acfg.$private.progName != null) {
/* we know the name of the executable, so this is an implicitly
* created config which is not shared. To prevent unnecessary
* clutter, we don't create the empty config file.
*/
/* TODO: should this be a .PHONY goal? */
out.write(goal + ": " + cfgBase + ".xdl\n\t@\n\n");
}
else {
out.write(goal + ": " + cfgBase
+ ".xdl\n\t@$(RM) $@\n\t@$(TOUCH) $@\n\n");
}
}
/*
* Changes to libraries SHOULD NOT trigger re-configuration BUT
* libraries must exist prior to running configuration; otherwise
* the configuration will fail (it insists that the libraries exist).
* Perhaps it is better to remove this check from configuration?
*
* Changes to libraries DO require re-link but this is covered by
* explicit dependencies generated by running the configuration
* script. The dependency for goal is here to allow one to specify
* goal as the build goal in a clean directory; without this
* dependency make does not know that it must first make libraries
* before making goal.
*/
out.write("\nifeq (,$(wildcard .libraries," + cmdSet + "))\n");
/* TODO handle absolute cfgBase (cfgBase in different package) */
out.write(goal + " " + cfgBase + ".c: .libraries," + cmdSet + "\n");
out.write("endif\n\n");
/* create rules to trigger generatation of configuration files */
var progName = acfg.$private.progName != null
? acfg.$private.progName : acfg.$private.name;
out.write(cfgBase + ".c " + cfgBase + ".h "
+ cfgBase + ".xdl: override _PROG_NAME := "
+ progName + "\n");
if (acfg.attrs.xsopts != null) {
out.write(cfgBase + ".c "
+ cfgBase + ".xdl: override _PROG_XSOPTS = "
+ acfg.attrs.xsopts + "\n");
}
out.write(cfgBase + ".c: " + cfgBase + ".cfg\n");
/* add prerequisite for .xdc.inc so releases that reference this file
* must wait for the confg step to complete
*/
out.write(cfgBase + ".xdc.inc: " + cfgBase + ".xdl\n");
/* ensure config is re-run if a config interface changes */
out.write(cfgBase + ".xdl " + cfgBase + ".c: .interfaces\n");
/*
* create rules to clean generated goals
*
* target-specific clean does not remove the generated cfg or dep file;
* otherwise, we would have to remake the makefile which can be very
* expensive for packages with lots of files or executables.
*/
out.write("\nclean:: clean," + cmdSet + "\n");
out.write("\t-$(RM) " + cfgBase + ".cfg\n");
out.write("\t-$(RM) " + cfgBase + ".dep\n");
out.write("\t-$(RM) " + cfgBase + ".c\n");
out.write("\t-$(RM) " + cfgBase + ".xdc.inc\n");
out.write("\nclean," + cmdSet + "::\n");
out.write("\t-$(RM) " + goal + "\n");
pkg.$private.generatedFiles[cfgBase + ".cfg"] = 1;
pkg.$private.generatedFiles[cfgBase + ".dep"] = 1;
pkg.$private.generatedFiles[cfgBase + ".c"] = 1;
pkg.$private.generatedFiles[cfgBase + ".xdc.inc"] = 1;
}
/*
* ======== _mkRelease ========
* A release is a zip (or tar) file containing all the files specified by
* the releases.$private.files list. If the list contains files with the
* extension ".xdc.inc", these files are recursively expanded to obtain
* additional files to be added to the tar; the .xdc.inc files are not part
* of the tar.
*
* Release archives are created as follows:
* 1. an initial manifest is specified in a build script by populating
* release.$private.files[string] with files that are to be
* archived *or* "*.xdc.inc" files that list additional files to be
* archived; "*.xdc.inc" files are created at later stages of the
* build.
* 2. _mkManifest() creates a top-level "*.xdc.inc" file named
* <rdir>/<rname>.xdc.inc, where <rdir> is the directory containing
* intermediate files related to a release, and <rname> is the name
* specified by the user. This file lists all files in the
* release.$private.files[] hash.
* 3. For every release, rel.js creates a release specific package.rel.xml
* in the directory <rdir>/<pkgdir>/package, where <rdir> is
* package/rel/<release_name> and <pkgdir> is the directory form of
* the package name.
* 4. The rule to make the archive, MKREL, is defined in xdc_rules.mak
* and performs the following steps:
* a. expand top-level "*.xdc.inc" file into *.manifest
* b. if there is a release script, run rcl.js on *.manifest;
* otherwise run tar on *.manifest
* c. add release-specific <pkdir>/package/package.rel.xml to the
* archive
*/
function _mkRelease(pkg, release, out)
{
var rdir = pkg.relDir;
out.write("\n.PHONY: release," + release.name + "\n");
var mname = rdir + release.name + ".xdc.inc";
var aname = release.$private.name; /* name of the archive file */
var aprefix = release.attrs.prefix;
var dname = rdir + aname + ".dep";
/* save name of top-level manifest file name to be recorded in .bld.xml */
release.$private.mname = mname;
/* indicate that manifest and dependencies files are generated */
pkg.$private.generatedFiles[mname] = 1;
pkg.$private.generatedFiles[dname] = 1;
if (_mkManifest(pkg, release, mname) > 0) {
pkg.$private.makefiles[pkg.$private.makefiles.length++] = {
name: dname,
src : mname
};
//out.write("ifneq (clean,$(MAKECMDGOALS))\n");
out.write("ifeq (,$(MK_NOGENDEPS))\n");
out.write("-include " + dname + "\n");
if (release.attrs.relScript != null) {
out.write("-include " + rdir + aname + ".rcl.dep\n");
}
out.write("endif\n");
/* generate rule to make unique release id file, .../package/package.rel.xml */
var idname = rdir + release.name + "/" + pkg.name.replace(/\./g, '/')
+ "/package/package.rel.xml";
/* since each release depends on it's id file, we add prerequisites
* here to ensure the release isn't built until all inputs are built
*/
for (var i in release.$private.files) {
/* ensure the id file isn't built before the release contents and
* the archive is rebuilt if one of its elems changes
*/
out.write(idname + ": " + i + "\n");
}
out.write(idname + ": .force\n");
out.write("\t@$(MSG) generating external release references $@ ...\n");
out.write("\t$(XS) $(JSENV) -f $(XDCROOT)/packages/xdc/bld/rel.js $(MK_RELOPTS) . $@\n\n");
/* generate rule to build archive (and gen deps) */
out.write(aprefix + aname + ": " + mname + " " + idname + "\n");
out.write('\t@$(MSG) making release file $@ "(because of $(firstword $?))" ...\n');
out.write("\t-$(RM) $@\n");
if (aname.match(/\.zip$/)) {
out.write("\t$(call MKRELZIP,");
}
else {
out.write("\t$(call MKRELTAR,");
}
out.write(mname.replace(/,/g, "$(comma)")
+ "," + dname.replace(/,/g, "$(comma)"));
if (release.attrs.relScript != null) {
/* $(comma) is defined in the makefiles to be ',' */
out.write(", "
+ release.attrs.relScript.replace(/,/g, "$(comma)"));
if (release.attrs.relScriptArgs != null) {
out.write(", "
+ release.attrs.relScriptArgs.replace(/,/g, "$(comma)"));
}
}
out.write(")\n\n");
out.write("\nrelease release," + release.name + ": all "
+ aprefix + aname +"\n");
/* generate rules to cleanup generated files */
out.write("clean:: .clean\n");
if (aprefix[0] != '.' || aprefix[1] != '.') {
/* don't remove files above the package's root directory */
out.write("\t-$(RM) " + aprefix + aname + "\n");
}
out.write("\t-$(RM) " + mname + "\n");
out.write("\t-$(RM) " + dname + "\n\n");
}
}
/*
* ======== _mkRepoMeta ========
*/
function _mkRepoMeta(pkg, repo)
{
var fileName = repo.name + "/.repo.xml";
try {
java.io.File(repo.name).mkdirs();
var file = new java.io.File(fileName);
file["delete"]();
file = new java.io.FileWriter(fileName);
file.write('<repository name="' + repo.name
+ '" providerId="' + pkg.name
+ '" producerId="' + escape(pkg.producerId)
+ '" />\n');
file.flush();
}
catch (e) {
throw new Error("Unable to create repository .repo.xml file for '"
+ repo.name + "'; " + e);
}
}
/*
* ======== _mkRepository ========
* Generates:
* release: <releaseTime>
* release,<releaseName>: <releaseTime>
*
* <releaseTime>: <releaseArchive>
* unzip $<
* touch $@
*
* <releaseArchive>:
* $(error ...)
*
* We create <releaseTime> empty files to enable us to track changes
* made to individual package archive files (located _outside_ this
* package).
*/
function _mkRepository(thisPkg, repository, out, relList)
{
_mkRepoMeta(thisPkg, repository);
for (var pname in repository.$private.packages) {
var pkg = repository.$private.packages[pname];
var relFile = _findReleaseArchive(pkg, repository);
var cpPkgDir = null;
if (relFile == null) {
cpPkgDir = _findReleaseBase(pkg);
if (cpPkgDir == null) {
throw new Error("Can't find the '" + pkg.rdesc.label
+ "' release of the package '" + pkg.name
+ "' to install/copy into the repository '"
+ repository.$private.name + "'.");
}
}
/* relTime represents the timestamp of relFile in repository */
var relTime = "package/."
+ repository.name.replace(/[\/\\]/g, ".")
+ "." + pname;
/* add package to the specified releases */
out.write("release: " + relTime + "\n");
var ra = repository.attrs.releases;
for (var i = 0; i < ra.length; i++) {
out.write("release," + ra[i].name + ": " + relTime + "\n");
out.write(ra[i].attrs.prefix + ra[i].$private.name + ": "
+ relTime + "\n");
}
if (cpPkgDir != null) {
/* create rule for copying package release into repository */
out.write(relTime + ": " + _escapeFilename(cpPkgDir) + "\n");
out.write("\t$(RM) $@\n");
out.write("\t@$(MSG) copying " + pname + " [ "
+ cpPkgDir + "] ...\n");
out.write("\t$(XDCCPP) \"$<\" " + repository.name + "\n");
out.write("\t$(TOUCH) $@\n\n");
continue; /* skip to next package to install */
}
/* create rule for installing package release into repository */
out.write(relTime + ": " + _escapeFilename(relFile) + "\n");
out.write("\t$(RM) $@\n");
out.write("\t@$(MSG) installing " + pname + " [release "
+ relFile + "] ...\n");
/* BUG: we should remove the package before installing it;
* otherwise it's possible that a file removed from more recent
* archives will not be deleted from the repository!
*/
if (pkg.rname.match(/\.zip$/)) {
out.write("\t$(UNZIP) -uo -qq \"$<\""
+ " -d " + repository.name + "\n");
}
else {
/* use --unlink-first to remove read-only files before extract */
/* use --recursive-unlink to remove directories before extract */
out.write("\t$(TAR) --unlink-first --recursive-unlink"
+ " --directory=" + repository.name
+ " --force-local -x"
+ (pkg.rname.match(/.*\.gz$/) ? "zf" : "f") + " \"$<\"\n");
}
out.write("\t$(TOUCH) $@\n\n");
/* create an empty rule for the release archive; this ensures
* that when we compute "built-with" dependencies we know that
* the package containing this archive is required and we don't get
* warnings about the package not needing to be declared as
* "required" in the package.xdc file.
*
* Note that if the archive is moved, all dependents will be
* re-created instead of make complaining that it doesn't know how
* to make the archive. Thus, we emit a better error message in each
* dependent; e.g., "rebuild and release package xyz"
*/
if (relList[relFile] == null) {
relList[relFile] = true; /* generate this rule just once */
out.write(_escapeFilename(relFile) + ":\n");
out.write("\t$(error $@ doesn't exist;"
+ " rebuild the " + pkg.rname + " release of package " + pname
+ "))\n\n");
}
}
out.write("clean::\n\t$(RMDIR) " + repository.name + "\n");
}
/*
* ======== _mkRule ========
* Convert simple command sequences into a make compatible sequence
* of rules; e.g., prepending a tab in front of each command.
*/
function _mkRule(cmds, goal, target)
{
/* insert remove of goal before commands to make it (so target doesn't
* need to)
*/
var rule = "\t$(RM) $@\n\t";
/* insert concise message (if it's supplied) */
if (cmds.msg != null) {
rule = rule.concat("@$(MSG) "
+ cmds.msg.replace(/\n/g, "\n\t@$(MSG) ") + "\n");
}
/* convert commands into separate make commands (add leading tab) */
if (cmds.cmds != null) {
rule = rule.concat("\t" + cmds.cmds.replace(/\n/g, "\n\t") + "\n");
}
else {
/* Bug 369984 - Crash if (buggy) target fails to set cmds */
java.lang.System.err.print("Warning: target '"
+ target.$name + "' didn't supply any commands for '"
+ goal + "'\n");
}
/* create environment for the commands */
if (cmds.envs != null) {
for (var i = 0; i < cmds.envs.length; i++) {
rule = rule.concat(goal + ": export " + cmds.envs[i] + "\n");
}
}
if (cmds.path != null && cmds.path.length > 0) {
var BuildEnvironment = xdc.om['xdc.bld.BuildEnvironment'];
var sep = BuildEnvironment.hostOS == BuildEnvironment.WINDOWS
? ";" : ":";
var path = "";
var prefix = "";
for (var i = 0; i < cmds.path.length; i++) {
var dir = cmds.path[i];
if (dir != null && dir.length > 0) {
path = path.concat(prefix + dir);
prefix = sep;
}
}
rule = rule.concat(goal + ": PATH:=" + path + "\n");
if (BuildEnvironment.hostOS == BuildEnvironment.WINDOWS) {
rule = rule.concat(goal + ": Path:=" + path + "\n");
}