blob: 907c688b8e443160fb9c61cf14f2792e0df1fdda [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--*/
/*
* ======== Main.xs ========
*
*! Revision History
*! ================
*! 17-Aug-2006 sasha renamed to Main.xs and removed getExecutables
*! 10-Aug-2006 sasha replaced utils.findFile with xdc.findFile
*! 11-Jul-2006 sasha moved pkg.close() before static$inits
*! 22-Jun-2006 sasha added getExecutables()
*! 21-Mar-2006 sasha created
*! 05-Apr-2006 mas added setPackage
*/
/*
* ======== Main.genFiles ========
*/
function genFiles(executable, prefix)
{
this.$private.$$cfgCapsule._postinit();
_genDependencyList();
if (Program.$$isrom) {
genRom(prefix);
}
var progGen = new Packages.xdc.services.intern.gen.Prog;
/* generate configuration files */
progGen.gen(prefix, executable, xdc.om, Program, true);
if (config.hasReportedError) {
throw xdc.$$XDCException(
"xdc.cfg.CONFIG_ERROR", "Error in configuration");
}
/*
* If an assembly, also gen linker files for executable's final link.
* Specifically we want a linker command file that includes the memory
* map. If the generated linker command file for the assembly is
* named 'XX_pYY.xdl', the new linker command file will be 'XX_pYY_x.xdl'.
*/
if (Program.$$isasm) {
var save$$isasm = Program.$$isasm;
Program.$$bind('$$isasm', 0);
progGen.gen(prefix + '_x', executable, xdc.om, Program, false);
Program.$$bind('$$isasm', save$$isasm);
}
if (config.hasReportedError) {
throw xdc.$$XDCException(
"xdc.cfg.CONFIG_ERROR", "Error in configuration");
}
/* generate config xml file, config graph, and debugger project files */
genXML(prefix + ".cfg.xml");
genDot(prefix + ".cfg.dot");
genRov(prefix + ".rov.xs");
var Rta = xdc.module('xdc.runtime.Rta');
Rta.genRta(prefix + ".rta.xml");
var genDebug = Program.gen.debuggerFiles;
if (genDebug == null) {
if (environment["xdc.cfg.gen.debuggerFiles"] != null) {
genDebug = environment["xdc.cfg.gen.debuggerFiles"] == "true";
}
}
if (genDebug
|| (genDebug == null
&& Program.build.profile.match(/debug/) != null)) {
genDbg(prefix, executable, Program);
}
}
/*
* ======== Main.init ========
*/
function init()
{
this.$private.$$usestk = null;
/* legacy tconf support */
this.$private.$$legacy = {prog: prog, Program: Program, config: config};
/* replace the default version of xdc.useModule with a cfg specific
* version.
*/
xdc.useModule = $$cfgUseModule;
this.$private.$$usemod = $$usemod;
Program = xdc.useModule('xdc.cfg.Program');
xdc.om.$$bind('$root', Program);
prog = Program; /* TODO: this should go away; find who uses it*/
}
var _Clock = xdc.useModule('xdc.services.global.Clock');
/*
* ======== Main.main ========
*/
function main(args)
{
function errorCheck(val) {
if (val != "") {
/* There are some errors or warnings. If only warnings are present,
* the configuration step continues.
*/
if (val.substring(0,2) == "@@") {
/* These are warnings/errors reported via $log functions */
printErrors();
if (xdc.om.$$errors.length > 0) {
throw new Error("Configuration failed!");
}
else {
xdc.om.$$warnings.length = 0;
}
}
else {
throw new Error(val);
}
}
}
if (args.length < 3) {
print("usage: xs xdc.cfg exe-name cfg-file gen-files-prefix [pkg-name]");
throw Error("Too few arguments");
}
if ($traceQuery()) {
_Clock.enable = true;
}
_Clock.reset();
var executable = args[0];
var cfgScript = args[1];
var prefix = args[2];
var pkgName = args[3];
xdc.$$make_om('cfg');
var cfgmodel = xdc.useModule('xdc.cfg.Main');
cfgmodel.init();
/* necessary for backward compatibility with tconf */
xdc.global.$$legacy = cfgmodel.$private.$$legacy;
_myTrace("config model initialization: $$phase - >1", 1, ["xdccore"]);
xdc.om.$$bind('$$phase', 1);
var ret = cfgmodel.setExecutable(cfgScript, pkgName);
errorCheck(ret);
_Clock.print("cfg model initialized.");
_myTrace("user script: $$phase - >2", 1, ["xdccore"]);
xdc.om.$$bind('$$phase', 2);
ret = cfgmodel.runCfg();
errorCheck(ret);
_Clock.print("cfg user script done.");
_myTrace("validation phase: $$phase - >6", 1, ["xdccore"]);
xdc.om.$$bind('$$phase', 6);
ret = cfgmodel.validate();
errorCheck(ret);
_myTrace("C config file generation: $$phase - >7", 1, ["xdccore"]);
xdc.om.$$bind('$$phase', 7);
cfgmodel.genFiles(executable, prefix);
_Clock.print("cfg generation done. Total cfg time: "
+ _Clock.getElapsedTime());
}
/*
* ======== Main.runCfg ========
*/
function runCfg()
{
return (this.runStringCfg(null));
}
/*
* ======== Main.runStringCfg ========
*/
function runStringCfg(script)
{
this.$private.$$cfgCapsule._userscript(this.$private.$$legacy, script);
try {
_myTrace("package closing phase: $$phase - >3", 1, ["xdccore"]);
xdc.om.$$bind('$$phase', 3);
closePackages();
sealConfig();
_myTrace("all config parameters sealed", 2, ["xdccore"]);
_myTrace("module$static$init phase: $$phase - >4", 1, ["xdccore"]);
xdc.om.$$bind('$$phase', 4);
configureModules();
_myTrace("instance$static$init phase: $$phase - >5", 1, ["xdccore"]);
xdc.om.$$bind('$$phase', 5);
configureInstances();
}
catch (e) {
if (e.toString() == "Error: xdc.fatal.error") {
return (getErrors());
}
else {
/* The code that examines $$warnings and $$errors must not be a
* function because it changes stack trace for the caught
* exception rethrown later.
* Error and warning messages are generated initially with ':::'
* as a separator between fields, so that XGCONF can parse them.
* When printed ':::' are replaced with ': '.
*/
if (xdc.om.$$warnings.length > 0) {
for (var i = 0; i < xdc.om.$$warnings.length; i++) {
print(xdc.om.$$warnings[i].replace(/\:\:\:/g, ": "));
}
}
if (xdc.om.$$errors.length > 0) {
for (var i = 0; i < xdc.om.$$errors.length; i++) {
print(xdc.om.$$errors[i].replace(/\:\:\:/g, ": "));
}
}
throw (e);
}
}
if (xdc.om.$$errors.length > 0) {
return (getErrors());
}
return ("");
}
/*
* ======== sealConfig ========
*/
function sealConfig()
{
for (var i = 0; i < $om.$packages.length; i++) {
var pkg = $om.$packages[i].$orig;
for (var j = 0; j < pkg.$modules.length; j++) {
var mod = pkg.$modules[j];
if ('PROXY$' in mod && mod.PROXY$) {
continue;
}
if (mod.$used && mod.$name != "xdc.cfg.Program") {
/* We leave internal config parameters unsealed because they
* should be accessed only by modules within this package
* because we keep changing them.
*/
var modParams = mod.$spec.getConfigs().listIterator();
var internMap = {};
while (modParams.hasNext()) {
var param = modParams.next();
if (param.isInternal() || param.isSys()) {
internMap[param.getName()] = 1;
}
}
for (var f in mod) {
if (!(f in internMap)) {
mod.$seal(f);
}
}
}
}
}
}
/*
* ======== Main.setExecutable ========
*/
function setExecutable(cfgScript, pkgName)
{
environment['config.scriptName'] = cfgScript;
var cfgFile = null;
if (pkgName == undefined) {
cfgFile = new java.io.File(cfgScript);
}
else {
cfgFile = new java.io.File(pkgName + "/" + cfgScript);
}
if (cfgFile == null) {
throw Error("Configuration file " + cfgScript + " is not found!");
}
var genCfg = utils.loadCapsule(cfgFile.getCanonicalPath());
this.$private.$$cfgCapsule = genCfg;
try {
genCfg._cfginit();
}
catch (e) {
if (e.toString() == "Error: xdc.fatal.error") {
return (getErrors());
}
else {
/* The code that examines $$warnings and $$errors must not be a
* function because it changes stack trace for the caught
* exception rethrown later.
* Error and warning messages are generated initially with ':::'
* as a separator between fields, so that XGCONF can parse them.
* When printed ':::' are replaced with ': '.
*/
if (xdc.om.$$warnings.length > 0) {
for (var i = 0; i < xdc.om.$$warnings.length; i++) {
print(xdc.om.$$warnings[i].replace(/\:\:\:/g, ": "));
}
}
if (xdc.om.$$errors.length > 0) {
for (var i = 0; i < xdc.om.$$errors.length; i++) {
print(xdc.om.$$errors[i].replace(/\:\:\:/g, ": "));
}
}
throw (e);
}
}
if (xdc.om.$$errors.length > 0 || xdc.om.$$warnings.length > 0) {
return (getErrors());
}
return ("");
}
/*
* ======== Main.validate ========
*/
function validate()
{
/* only the throws by $$logFatal are caught here */
try {
/* package validate() */
for (var i = xdc.om.$packages.length - 1; i >= 0; i--) {
var pkg = xdc.om['xdc.IPackage'].Module(xdc.om.$packages[i]);
_myTrace("validating " + pkg.$name, 2, ["xdccore"]);
for (var j = 0; j < pkg.$modules.length; j++) {
if (pkg.$modules[j].$used) {
pkg.$modules[j].module$validate();
}
}
pkg.validate();
}
}
catch (e) {
if (e.toString() == "Error: xdc.fatal.error") {
return (getErrors());
}
else {
/* The code that examines $$warnings and $$errors must not be a
* function because it changes stack trace for the caught
* exception rethrown later.
* Error and warning messages are generated initially with ':::'
* as a separator between fields, so that XGCONF can parse them.
* When printed ':::' are replaced with ': '.
*/
if (xdc.om.$$warnings.length > 0) {
for (var i = 0; i < xdc.om.$$warnings.length; i++) {
print(xdc.om.$$warnings[i].replace(/\:\:\:/g, ": "));
}
}
if (xdc.om.$$errors.length > 0) {
for (var i = 0; i < xdc.om.$$errors.length; i++) {
print(xdc.om.$$errors[i].replace(/\:\:\:/g, ": "));
}
}
throw (e);
}
}
if (xdc.om.$$errors.length > 0) {
return (getErrors());
}
_Clock.print("cfg model validate done.");
/* check version consistency and fail on any discrepancy */
var result = checkVersions(environment["xdc.cfg.check.exclude"]);
_Clock.print("cfg package version checks done.");
if (result.msg != "") {
if (environment["xdc.cfg.check.fatal"] == "false"
|| result.eCount == 0) {
print("Warning: " + result.msg);
}
else {
return (result.msg);
}
}
if (config.hasReportedError) {
return ("Validation error");
}
if (xdc.om.$$warnings.length > 0) {
return (getErrors());
}
return ("");
}
/*---------------- private functions ----------------- */
/* From this point on, 'this' refers to the file scope, not module Main */
/*
* ======== checkVersions ========
* Validate that the "built-with" versions of imported packages are
* compatible with their actual versions.
*
* Returns an object with the properties wCount, eCount, and msg. If there
* is no incompatibility msg == ""; otherwise msg is a meaningful message
* describing all incompatibilities found. wCount is a count of warnings
* and eCount is a count of errors
*/
function checkVersions(exclude)
{
var result = {eCount: 0, wCount: 0, msg: ""};
var msgs = {};
/* optionally subset the list of imported packages to check */
var pkgList = [];
if (exclude != null && exclude != undefined) {
for (var i = 0; i < $om.$packages.length; i++) {
var pkg = $om.$packages[i].$orig;
var tmp = pkg.$name.match(exclude);
if (tmp == null || tmp[0] != pkg.$name) {
// print("including package " + pkg.$name);
pkgList.push($om.$packages[i]);
}
else {
// print("excluding package " + pkg.$name);
}
}
}
else {
pkgList = $om.$packages;
}
/* For every package pkg imported in this configuration, check that
* the target version used by pkg is compatible with the target
* version used by Program.
*/
if (Program.build.target.version != null) {
var pa = Program.build.target.version.split("{");
for (var i = 0; i < pkgList.length; i++) {
var pkg = pkgList[i].$orig;
if (pkg.build.libraries.length == 0) {
continue; /* if pkg has no libraries, don't check it */
}
/* get target version info from pkg */
var trgs = Packages.xdc.services.global.Vers.getTargetReferences(
pkg.packageBase + "/package/package.bld.xml");
/* verify compatibility with Program.build.target version */
for (var j = 0; j < trgs.length; j++) {
var ta = ("" + trgs[j]).split("{");
if (ta[0] == pa[0]) {
var emsg = isCompatible(ta[0], pa[1], ta[1], pkg.$name);
if (emsg != null) {
result.eCount += emsg.eCount;
result.wCount += emsg.wCount;
if (msgs[ta[0]] == null) {
msgs[ta[0]] = "xdc.cfg.INCOMPATIBLE_TARGET_VERSION:"
+ " current target '" + ta[0] + "' [" + pa[1]
+ "] is not compatible with targets used to "
+ "build the following packages";
}
msgs[ta[0]] += "; " + "package " + pkg.$name
+ " [in " + pkg.packageBase + "]"
+ " was built using '" + ta[0]
+ "' [" + ta[1] + "]\n";
}
break;
}
}
/*
* Note that we do *not* ensure that the same targets are used.
* We only verify that for the targets used their keys indicate
* that they are compatible. This allows a package to use a
* different target to build a library that is contributed to
* this executable; e.g., BIOS can use a single C55 target that
* supports 1510, 1610, and 1710 but the application can use a
* target that assumes the 1710.
*/
if (j >= trgs.length) {
; /* package pkg was not built with the same target! */
}
}
}
/* For every package pkg imported by this configuration, check that
* the packages used to build pkg are compatible with the packages
* imported by this configuration.
*/
for (var i = 0; i < pkgList.length; i++) {
var pkg = pkgList[i].$orig;
/* get packages referenced by pkg during its build */
// print(" imported package " + pkg.$name);
var refs = Packages.xdc.services.global.Vers.getReferences(
pkg.packageBase + "/package/package.rel.xml");
/* for each referenced package, check consistency with imported set */
for (var j = 0; j < refs.length; j++) {
var pa = ("" + refs[j]).split("{");
// print(" built with: " + refs[j]);
for (var k = 0; k < pkgList.length; k++) {
var tmp = pkgList[k].$orig;
/* if ref[j] is in the imported set ... */
if (pa[0] == tmp.$name) {
/* build tmp's version string from vers number array */
var vers = "";
if (tmp.$vers != null) {
vers = tmp.$vers.slice(0,4).join(", ");
}
var emsg = isCompatible(tmp.$name, vers, pa[1], pkg.$name);
if (emsg != null) {
result.eCount += emsg.eCount;
result.wCount += emsg.wCount;
if (msgs[tmp.$name] == null) {
msgs[tmp.$name] = "incompatible use of package '"
+ tmp.$name + "' [in " + tmp.packageBase
+ "]: version of the loaded package '"
+ tmp.$name + "' is [" + vers + "]";
}
msgs[tmp.$name] += ", while '" + pkg.$name + "' [in "
+ pkg.packageBase + "]" + " was built with '"
+ tmp.$name + "' [" + pa[1] + "]";
}
}
}
}
}
var sep = "";
for (var m in msgs) {
result.msg += sep + msgs[m];
sep = "; ";
}
if (result.msg != "" && environment["xdc.cfg.check.fatal"] != "false"
&& result.eCount > 0) {
result.msg += "\nAdding the following line to your program "
+ "configuration script:\n\tenvironment['xdc.cfg.check.fatal'] = "
+ "'false';\nconverts this incompatibility error into a warning. "
+ "Please, check the documentation for the package xdc.cfg for "
+ "more information.";
}
return (result);
}
/*
* ======== _genDependencyList ========
*
*/
function _genDependencyList()
{
var genList = [];
/* Here we need to go through xdc.om.$require map and put the packages
* from genList in order determined by dependencies from xdc.om.$require.
*/
// print("_genDependencyList: (requires)"); for (var key in xdc.om.$require) print(" " + key);
// _Clock.print("tsort:start (" + xdc.om.$require.length + " edges, " + xdc.om.$packages.length + " pkgs)");
while (xdc.om.$require.length > 0) {
/* select a package with no requirements; i.e., a leaf node */
var selected = null;
for (var key in xdc.om.$require) {
var candidate = xdc.om.$require[key];
//print("candidate " + candidate);
var leaf = 1;
for (var key2 in xdc.om.$require) {
if (key2.split(/\s/)[0] == candidate) {
//print("found dependency on " + xdc.om.$require[key2]);
leaf = 0;
break;
}
}
if (leaf == 1) {
selected = candidate;
break;
}
}
if (selected == null) {
/* There is a cycle in remaining dependencies. Find a package
* from the cycle and use it as 'selected'. If there is a cycle,
* the program is bogus anyway, so we just try to recover using a
* random ordering and see if it goes through.
* There could be some packages remaining that are not a part of
* the cycle. If we just follow the cycle for the number of steps
* equal to the number of packages, we are going to end up with one
* of the packages from the cycle. That way, when we remove one of
* the dependencies for a package from the cycle, we will actually
* break the cycle (or one of the cycles).
*/
selected = xdc.om.$require[0];
var cycleStart;
var counter = xdc.om.$packages.length - genList.length;
while (counter > 0) {
for (var key in xdc.om.$require) {
if (key.split(/\s/)[0] == selected) {
selected = xdc.om.$require[key];
cycleStart = key;
counter--;
}
}
}
_myTrace(
"warning: cycle detected involving the following packages:", 1,
["packageLoad"]);
var next = xdc.om.$require[cycleStart];
_myTrace(" " + cycleStart.split(/\s/)[0] + " --> " + next, 1,
["packageLoad"]);
while (next != selected) {
for (var key in xdc.om.$require) {
if (key.split(/\s/)[0] == next) {
next = xdc.om.$require[key];
_myTrace(" " + key.split(/\s/)[0] + " --> " + next, 1,
["packageLoad"]);
break;
}
}
}
/* Promote dependencies of the removed package, so they
* are not completely lost when the cycle is broken.
* Normally when a package is removed in this function it
* is a leaf node -- it has no remaining dependencies. But in
* this section we are breaking a cycle -- the package we will
* remove still has remaining dependencies. The following code
* essentially grafts together the incoming and outgoing
* edges of the package about to be removed. By doing so
* we at least retain the derivable constraints between the
* packages "above" and "below" the removed package.
*/
/* first find all the dependencies of the package about to
* be removed (call these "child" packages)
*/
var children = [];
for (var key in xdc.om.$require) {
var parent = key.split(/\s/)[0];
if (parent == selected) {
children.push(xdc.om.$require[key]);
}
}
/* next find all the packages that depend on the one about
* to be removed (call these "grandparent" packages)
*/
for (var key in xdc.om.$require) {
var grandparent = key.split(/\s/)[0];
var parent = xdc.om.$require[key];
if (parent == selected) {
/* and add explicit dependencies from the grandparent
* packages to the child packages.
*/
for each (var child in children) {
if (grandparent == child) {
/* no need to record a dependency on yourself */
continue;
}
/* record the dependency */
xdc.om.$require[grandparent + " " + child] = child;
}
}
}
}
/* append the selected leaf to the end of genList */
genList.push(xdc.om[selected]);
xdc.om[selected].$$bind('$$genflg', 1);
/* Remove all elements from $require that have 'selected' as a
* second element; i.e., edges that reference the selected leaf
*/
for (var key in xdc.om.$require) {
if (xdc.om.$require[key] == selected) {
/* A very important assumption here is that 'key' shows up in
* the remaining dependencies as 'value. That must be true
* because someone must have loaded 'key', and since we haven't
* moved 'key' into the final list, that dependency hasn't been
* removed yet.
*/
//print("deleted " + key.split(/\s/)[0] + " -> " + key.split(/\s/)[1]);
delete xdc.om.$require[key];
}
}
}
/* get $homepkg out of the list and add it at the end to ensure its
* libraries will end up at the top of the linker command file.
*/
for (var i = 0; i < genList.length; i++) {
if (genList[i] == xdc.om.$homepkg) {
genList.splice(i, 1);
genList.push(xdc.om.$homepkg);
break;
}
}
// print("_genDependencyList:");for (var i in genList) print(" " + genList[i]);
/* update the official list of packages */
xdc.om.$packages.$self = genList;
// _Clock.print("tsort:done");
}
/*
* ======== closePackages ========
*
* call pkg.close() for all packages in the configuration
*/
function closePackages ()
{
var curIndex = 0;
var closeStack = [];
var saveCurPkg = xdc.om.$curpkg;
for (;;) {
/* transfer current set of packages to a stack */
for (var i = curIndex; i < xdc.om.$packages.length; i++) {
closeStack.push(xdc.om.$packages[i]);
}
curIndex = xdc.om.$packages.length;
if (closeStack.length == 0) {
break;
}
/* close the top-most package */
var curpkg = xdc.om['xdc.IPackage'].Module(closeStack.pop());
/* Setting $curpkg to curpkg.$name must precede the useModule calls */
xdc.om.$$bind('$curpkg', curpkg.$name);
for each (var mod in curpkg.$modules) {
/* Modules that never made it to $$usemod or went through it only
* partially are calling $$usemod again.
*/
if (mod.$used &&
(!('$$cfgPhase' in mod) || (mod.$$cfgPhase == 1))) {
xdc.useModule(mod);
}
}
_myTrace("calling " + curpkg.$name + ".close()", 1, ["xdccore"]);
curpkg.close();
}
xdc.om.$$bind('$curpkg', saveCurPkg);
/* special call into xdc.runtime package to "finalize" the
* modules in this configuration; e.g., propagate logggers, gates,
* and heap managers to all runtime modules in the configuration
*
* Warning: no new modules can be added after this point; otherwise the
* common settings will not be properly initialized!
*/
xdc.loadPackage('xdc.runtime').$capsule.finalize();
Program.$capsule._close.apply(Program);
}
/*
* ======== configureInstances ========
*
* call instance$static$init for all instances
*/
function configureInstances()
{
/* construct instance meta-objects */
for (var i = 0; i < xdc.om.$modules.length; i++) {
var mod = xdc.om.$modules[i].$orig;
if (!mod.$used || mod.$hostonly) {
continue;
}
for (var aname in {$instances: null, $objects: null}) {
var arr = mod[aname];
for (var j = 0; j < arr.length; j++) {
var inst = arr[j];
if ('$$phase' in inst && inst.$$phase >= 5) {
continue;
}
inst.$$bind('$$phase', 5);
_myTrace("calling " + mod.$name + ".instance$static$init for "
+ "instance " + aname, 2, ["xdccore"]);
mod.__initObject(inst);
}
}
}
}
/*
* ======== configureModules ========
*
* call module$static$init for all modules
*/
function configureModules()
{
var cfgModel = xdc.module('xdc.cfg.Main');
/* propagate default profile to each imported package */
for (var i = xdc.om.$packages.length - 1; i >= 0; i--) {
var pkg = xdc.om['xdc.IPackage'].Module(xdc.om.$packages[i]);
if (pkg.profile == null) {
pkg.profile = Program.build.profile;
}
}
/* propagate assertions of modules used */
cfgModel.$private.$$usestk = [];
for (var i = 0; i < xdc.om.$modules.length; i++) {
var mod = xdc.om.$modules[i];
//mod.$seal();
if (mod.$used) {
_myTrace("calling " + mod.$orig.$name + ".module$static$init", 2,
["xdccore"]);
mod.$orig.module$static$init(
'$object' in mod ? mod.$object : null, mod.$orig);
}
}
}
/*
* ======== genDbg ========
* Generate debug support files for IDEs such as CCS.
*
* Params:
* cfgName - the name of the generated configuration script
* exeName - the name of the output executable
* prog - the configured xdc.cfg.Program object
*
* both cfgName and exeName are relative to the directory containing
* package.bld.
*/
function genDbg(cfgName, exeName, prog)
{
var template;
var fPattern;
var fname;
// print("cfgName = '" + cfgName + "', " + "exeName = '" + exeName);
if (prog.build.target.debugGen != null) {
template = prog.build.target.debugGen.execTemplate;
fPattern = prog.build.target.debugGen.execPattern;
}
if (template != null) {
var bldUtils = xdc.module('xdc.bld.Utils');
/* expand the output file name pattern */
fname = bldUtils.expandDbgName(cfgName, exeName, fPattern);
/* expand template */
// print("template file '" + template + "', generating '" + fname + "'");
var t = xdc.loadTemplate(template);
t.genFile(fname, prog, [exeName]);
}
return (fname);
}
/*
* ======== genDot ========
* Generate Dot file that contains information about the configuration.
*/
function genDot(outputFileName)
{
function toLabel(name)
{
return (name.replace(/\./g, '_'));
}
/* if outputFileName is null, write to stdout */
var out;
if (outputFileName != null) {
var file = new java.io.File(outputFileName);
file["delete"]();
out = new java.io.FileWriter(outputFileName);
out = new java.io.BufferedWriter(out);
}
else {
out = new java.io.OutputStreamWriter(java.lang.System.out);
}
out.write('digraph configuration {\n');
out.write(' size="7.5,10";\n');
out.write(' rankdir=LR;\n');
out.write(' ranksep=".50 equally";\n');
out.write(' concentrate=true;\n');
out.write(' compound=true;\n');
out.write(' label="\\nConfiguration for '
+ Program.buildPackage + '/' + Program.name + '"\n');
/* Create a distinguished subgraph that represents the config script */
out.write(' node [font=Helvetica, fontsize=14, fontcolor=black];');
out.write(' subgraph cluster0 {label=""; __cfg [label="'
+ Program.build.cfgScript + '", color=white, fontcolor=blue];\n');
out.write(' node [font=Helvetica, fontsize=10];');
/* For every package imported by this configuration, create
* sub-graph cluster of modules within the package.
*/
pkgNodes = [];
for (var i = 0; i < $om.$packages.length; i++) {
var pkg = $om.$packages[i].$orig;
var vers = "";
if (pkg.$vers != null) {
vers = pkg.$vers.join(",");
}
out.write(' subgraph cluster' + (i + 1) + ' {\n');
out.write(' label="";\n');
/* create top node in the cluster to represent the pkg as a whole */
var pnname = toLabel(pkg.$name);
out.write(' '+ pnname + '__top [shape=box,label="'
+ pkg.$name + '\\n' + vers + '", color=white];\n');
/* create bottom node in the cluster to represent pkg as a whole */
if (pkg.$modules.length > 0) {
out.write(' '+ pnname + '__bot [shape=point,label=""'
+ ', style=invis];\n');
pkgNodes[i] = {top: pnname + '__top', bot: pnname + '__bot'};
}
else {
pkgNodes[i] = {top: pnname + '__top', bot: pnname + '__top'};
}
/* create nodes for each module in the package pkg */
for (var j = 0; j < pkg.$modules.length; j++) {
var mod = pkg.$modules[j];
var nname = toLabel(mod.$name);
var mname = mod.$name + "";
if (mname.indexOf(pkg.$name + '.') == 0) {
mname = mname.substr(pkg.$name.length + 1);
}
/* make a node for each module; gray are used, white aren't */
if (mod.$used) {
out.write(' ' + nname + ' [style=filled,fillcolor='
+ 'lightgray, label="' + mname + '"];\n');
}
else {
// out.write(' ' + nname + ' [style=filled,fillcolor='
// + 'white, label="' + mname + '"];\n');
continue;
}
/* top invisibly points to all modules in the package */
out.write(' ' + pkgNodes[i].top + ' -> '
+ nname + '[style=invis];\n');
/* and all modules in the package invisibly point to bot */
out.write(' ' + nname + ' -> '
+ pkgNodes[i].bot + '[style=invis];\n');
/* each module points to every other module it "uses" */
for (var k = 0; k < mod.$uses.length; k++) {
out.write(' ' + nname + ' -> '
+ toLabel(mod.$uses[k].$name) + '\n');
}
}
out.write(' }\n'); /* end package subgraph */
}
out.write(' }\n'); /* end cfg script sub-graph */
/* For every package pkg imported in this configuration, draw an edge
* from the bottom of the cluster to the target version used by pkg.
*/
out.write(' node [font=Helvetica, fontsize=10];\n');
var pa = Program.build.target.version.split("{");
for (var i = 0; i < $om.$packages.length; i++) {
var pkg = $om.$packages[i].$orig;
/* get target version info from pkg */
var trgs = Packages.xdc.services.global.Vers.getTargetReferences(
pkg.packageBase + "/package/package.bld.xml");
/* find the target used by this config, and output its version */
var tname = null;
for (var j = 0; j < trgs.length; j++) {
var ta = ("" + trgs[j]).split("{");
if (ta[0] == pa[0]) {
/* create a unique node name for each target version */
tname = toLabel(ta[0]) + '__'
+ ta[1].replace(/\./g, '__').replace(/,/g,'_');
/* create the target node */
out.write(' ' + tname
+ ' [shape=record,label="' + ta[0] + '|' + ta[1]
+ '",style=filled, fillcolor=lightgrey];\n');
break;
}
}
/* draw an edge to the target used by the package */
if (tname != null) {
out.write(' ' + pkgNodes[i].bot
+ ' -> ' + tname + ' [ltail=cluster' + (i + 1) + '];\n');
}
}
out.write('}\n');
out.flush();
if (outputFileName != null) {
out.close();
}
}
/*
* ======== cfg.genRom ========
*/
function genRom( prefix )
{
/* generate <prefix>.romasm.xs */
var Text = xdc.module('xdc.runtime.Text');
var str = "";
var strings = [];
for (var i = 0; i < Text.charTab.length; i++) {
if (Text.charTab[i] != 0) {
str += String.fromCharCode(Text.charTab[i]);
}
else {
strings.push(str);
str = "";
}
}
var o = {
asmName: Program.name,
cfgScript: Program.build.cfgScript,
isasm: Program.$$isasm,
isrom: Program.$$isrom,
fixedCodeAddr: Program.fixedCodeAddr,
fixedDataAddr: Program.fixedDataAddr,
modules: [],
sectNamesC: [],
sectNamesV: [],
nodeTab: xdc.module('xdc.runtime.Text').nodeTab,
charTab: strings,
};
var bset = {};
o.objfile = 'package/cfg/' + o.asmName + '.obj';
for (var i = 0; i < xdc.om.$modules.length; i++) {
var mod = xdc.om.$modules[i];
if (mod.$hostonly || !mod.$used || mod.$$scope == -1) {
continue;
}
o.modules.push(
{name: mod.$name, romPatchTable: mod.common$.romPatchTable});
if (mod.PROXY$) {
continue;
}
var cname = mod.$name.replace(/\./g, '_') + '_';
if (mod.common$.romPatchTable) {
o.sectNamesC.push(cname + 'Module__MTAB__C');
}
o.sectNamesV.push(cname + 'Module__root__V');
if ('$object' in mod) {
o.sectNamesV.push(cname + 'Module__state__V');
}
if (mod.$$instflag) {
if (mod.$$romcfgs.indexOf('|Object__PARAMS|') == -1) {
o.sectNamesC.push(cname + 'Object__PARAMS__C');
}
if (mod.common$.instanceHeap) {
o.sectNamesC.push(cname + 'Object__DESC__C');
}
}
if (mod.$$iflag && mod.common$.fxntab) {
for (var u = mod.$spec.getSuper(); u != null; u = u.getSuper()) {
var cn = String(u.getQualName()).replace(/\./g, '_') + '_';
if (!bset[cn]) {
o.sectNamesC.push(cn + 'Interface__BASE__C');
bset[cn] = true;
}
}
}
for each (var mc in mod.$$mcfgs) {
if (mod.$$romcfgs.indexOf('|' + mc + '|') == -1) {
o.sectNamesC.push(cname + mc + '__C');
}
}
}
var fn = prefix + '.romasm.xs';
var file = new java.io.File(fn);
file["delete"]();
xdc.recapObject(o, fn);
Program.$capsule._romAsm = o;
/* generate <prefix>.rename */
var fos = new java.io.PrintWriter(prefix + '.rename');
for (var i = 0; i < xdc.om.$modules.length; i++) {
var mod = xdc.om.$modules[i];
var unit = mod.$spec;
var cname = String(unit.getQualName()).replace(/\./g, '_') + '_';
if (unit.isInter() || unit.isMeta()
|| !mod.$used || mod.$$scope == -1) {
continue;
}
for each (var fxn in mod.$spec.getFxns().toArray()) {
if (fxn.isSys()) {
continue;
}
var fn = '_' + cname + fxn.getName();
fos.print(fn + '__E ' + fn + '__ER\n');
fos.print(fn + '__R ' + fn + '__E\n');
if (fxn.isVarg()) {
var fn = '_' + cname + fxn.getName() + '_va';
fos.print(fn + '__E ' + fn + '__ER\n');
fos.print(fn + '__R ' + fn + '__E\n');
}
}
}
for each (var sn in Program.$capsule._romAsm.sectNamesC) {
fos.print('_' + sn + ' _' + sn + 'R\n');
}
for each (var sn in Program.$capsule._romAsm.sectNamesV) {
fos.print('_' + sn + ' _' + sn + 'R\n');
}
fos.close();
}
/*
* ======== genRov ========
*
* Recapture configuration information for rov model
*/
function genRov(outputFileName)
{
var o = {};
o.build = Program.build;
o.$modules = {};
for (var i = 0; i < xdc.om.$modules.length; i++) {
var m = xdc.om.$modules[i];
if (!m.$hostonly && m.$used && !m.PROXY$) {
o.$modules[m.$name] = m;
o.$modules["#" + m.Module__id] = m;
}
}
if (outputFileName != null) {
var file = new java.io.File(outputFileName);
file["delete"]();
}
xdc.recapObject(o, outputFileName);
}
/*
* ======== genXML ========
* Generate XML file that contains information about the configuration.
*/
function genXML(outputFileName)
{
/* if outputFileName is null, write to stdout */
var out;
if (outputFileName != null) {
var file = new java.io.File(outputFileName);
file["delete"]();
out = new java.io.FileWriter(outputFileName);
out = new java.io.BufferedWriter(out);
}
else {
out = new java.io.OutputStreamWriter(java.lang.System.out);
}
out.write('<?xml version="1.0" encoding="UTF-8"?>\n');
// out.write('<!DOCTYPE release SYSTEM "configuration.dtd">\n');
out.write('<!-- This file conforms to the DTD xdc/cfg/configuration.dtd -->\n');
out.write('<configuration name="'
+ Program.buildPackage + '/' + Program.name + '"\n '
+ 'platformName="' + Program.platformName + '"\n '
+ 'globalSection="' + Program.globalSection + '"\n '
+ 'sysStack="' + Program.sysStack + '"\n '
+ 'stack="' + Program.stack + '"\n '
+ 'heap="' + Program.heap + '"\n '
+ 'argSize="' + Program.argSize + '"\n '
+ 'execCmd="' + escape(Program.execCmd) + '"\n '
+ 'endian="' + Program.endian + '"\n '
+ 'codeModel="' + Program.codeModel + '"\n '
+ 'dataModel="' + Program.dataModel + '">\n');
/* For every package pkg imported in this configuration, output
* the target version used by the pkg.
*/
out.write('<targets>\n');
var pa = Program.build.target.version.split("{");
for (var i = 0; i < $om.$packages.length; i++) {
var pkg = $om.$packages[i].$orig;
/* get target version info from pkg */
var trgs = Packages.xdc.services.global.Vers.getTargetReferences(
pkg.packageBase + "/package/package.bld.xml");
/* find the target used by this config, and output its version */
for (var j = 0; j < trgs.length; j++) {
var ta = ("" + trgs[j]).split("{");
if (ta[0] == pa[0]) {
out.write(' <target name="' + ta[0] + '"\n');
out.write(' version="' + ta[1] + '"\n');
out.write(' usedby="' + pkg.$name + '"/>\n');
break;
}
}
}
out.write('</targets>\n');
/* For every package pkg imported by this configuration, output its
* version and its modules used list, and for each module, its uses
* list.
*/
out.write('<imports>\n');
for (var i = 0; i < $om.$packages.length; i++) {
var pkg = $om.$packages[i].$orig;
var vers = "";
if (pkg.$vers != null) {
vers = pkg.$vers.join(",");
}
out.write(' <package name="' + pkg.$name + '" version="'
+ vers + '" repository="' + pkg.packageRepository + '">\n');
/* dump all modules (except for proxies) */
for (var j = 0; j < pkg.$modules.length; j++) {
var mod = pkg.$modules[j];
if ('PROXY$' in mod && mod.PROXY$) {
continue;
}
out.write(' <module name="' + mod.$name + '"\n');
out.write(' used="' + mod.$used + '"\n');
out.write(' uses="');
var prefix = "";
for (var k = 0; k < mod.$uses.length; k++) {
out.write(prefix + mod.$uses[k].$name);
prefix = ';';
}
out.write('">\n');
/* if the module is used, dump its config settings */
if (mod.$used) {
var m = xdc.om[mod.$name].Module(mod);
for (f in m) {
var type = typeof m[f];
var value = String(m[f]);
var xml = null;
if (m[f] != null && type == "object") {
if ("$type" in m[f]) {
type = escape("" + m[f].$type);
}
if ("$toText" in m[f]) {
value = m[f].$toText();
try {
xml = ("$toXml" in m[f]) ? m[f].$toXml() : null;
}
catch (e) {
// print("failed to gen XML for " + m + "." + f);
}
}
}
out.write(' <feature name="' + f
+ '" type="' + type
+ '" value="' + escape(value) + '"');
if (xml == null) {
out.write('/>\n');
}
else {
out.write(">" + xml + "\n </feature>\n");
}
}
}
out.write(' </module>\n');
}
out.write(' </package>\n');
}
out.write('</imports>\n');
out.write('</configuration>\n');
out.flush();
if (outputFileName != null) {
out.close();
}
}
/*
* ======== getErrors ========
* Create a string out of all warnings and error messages separated by '@@'.
* The interface between XGCONF and cfg server does not allow arrays, so we
* had to use one long string.
*/
function getErrors()
{
var ret = "@@";
if (xdc.om.$$warnings.length > 0) {
ret = ret + xdc.om.$$warnings.join("@@");
}
if (xdc.om.$$errors.length > 0) {
if (xdc.om.$$warnings.length > 0) {
ret = ret + "@@";
}
ret = ret + xdc.om.$$errors.join("@@");
}
return (ret);
}
/*
* ======== isCompatible ========
* Check compatibility when package rname uses version bvers of package
* pname during rname's build but version avers is being used in the
* current configuration.
*
* Params:
* pname - package name
* avers - imported (actual) version of package pname
* bvers - version referenced during rname's build
* rname - name of package referencing package pname
*
* Returns:
* null if versions avers and bvers are compatible; otherwise it returns
* a string starting with "Error" or "Warning" that indicates the
* failure.
*/
function isCompatible(pname, avers, bvers, rname)
{
// print("checking compatibility between " + pname + " (vers = " + avers + ") and vers = " + bvers + " used by " + rname);
if (avers == bvers) {
return (null);
}
var result = {wCount: 0, eCount: 0, msg: ""};
/* versions differ, check version numbers */
var ava = avers.split(',');
var bva = bvers.split(',');
/* if one is a version number and the other isn't, assume incompatible */
if (ava == null || bva == null) {
result.wCount = 1;
result.msg = "Warning: can't determine version of " + pname
+ " or the version of " + pname + " used to build " + rname;
return (result);
}
/* otherwise, we need to interpret the version number to decide */
for (var i = 0; i < bva.length; i++) {
/* convert string digits to numbers */
var a = ava[i] - 0;
var b = bva[i] - 0;
/* if digits differ (or as non-numeric strings they don't match) */
if (a != b || (isNaN(a - b) && ava[i] != bva[i])) {
switch (i) {
case 0: { /* incompatible releases */
result.eCount = 1;
result.msg = "Error: re-write of "+ rname + " is required";
return (result);
}
case 1: { /* source compatible release */
result.eCount = 1;
result.msg = "Error: re-build of "+ rname + " is required";
return (result);
}
case 2: { /* API radius */
if (a < b) {
result.wCount = 1;
result.msg = "Warning: the version of " + pname
+ " included in this configuration is older than the version of "
+ pname + " used to build " + rname;
return (result);
}
break;
}
case 3: { /* binary 100% compatible release */
break;
}
}
return (null);
}
}
/* if we get here, the required version is prefix of the avail version */
return (null);
}
/*
* ======== printErrors ========
* Print all warnings and error messages accumulated in $$warning and $$errors
* arrays.
*/
function printErrors()
{
/* Error and warning messages are generated initially with ':::' as a
* separator between fields, so that XGCONF can parse them. When printed
* ':::' are replaced with ': '.
*/
if (xdc.om.$$warnings.length > 0) {
for (var i = 0; i < xdc.om.$$warnings.length; i++) {
print(xdc.om.$$warnings[i].replace(/\:\:\:/g, ": "));
}
}
if (xdc.om.$$errors.length > 0) {
for (var i = 0; i < xdc.om.$$errors.length; i++) {
print(xdc.om.$$errors[i].replace(/\:\:\:/g, ": "));
}
}
}
/*
* ======== $$usemod ========
*/
function $$usemod( mod, cycles )
{
mod.$used = true;
/* We need to know when the module was loaded for the first time, except
* for cases when the user's script loads a module when we treat that
* as the time when module was loaded.
*/
if (!('$$usedPhase' in mod) || xdc.om.$$phase == 2) {
mod.$$bind('$$usedPhase', xdc.om.$$phase);
}
if (xdc.om.$$phase >= 4) {
print("WARNING: Can't call useModule() now:", mod.$name);
}
if (!('$$scope' in mod)) {
mod.$$bind('$$scope', (xdc.om.$$phase < 3) ? 1 : 0);
}
if (!('$$cfgPhase' in mod)) {
mod.$$bind('$$cfgPhase', 0);
}
if (mod.$$cfgPhase == -1 || mod.$$cfgPhase == 2) {
return (mod);
}
if (xdc.om.$$phase < 3) {
mod.$$bind('$$cfgPhase', 1);
return mod;
}
mod.$$bind('$$cfgPhase', -1);
_myTrace("calling " + mod.$orig.$name + ".module$use", 2, ["moduleUse"]);
mod.$orig.module$use();
if (!mod.$hostonly && '$$proxies' in mod && !mod.PROXY$) {
for each (var p in mod.$$proxies) {
var pud = mod[p + '$proxy'];
$$usemod(pud);
if (pud.delegate$) {
$$usemod(pud.delegate$, true);
}
}
}
mod.$$bind('$$cfgPhase', 2);
return mod;
}
/*
* ======== cfgUseModule ========
* Use a module in a package
*/
function $$cfgUseModule(qname, cycles)
{
/* xdc.om.$curpkg contains the name of the current package. We keep track
* of it to be able to record dependencies between packages. We need to
* keep saving and retrieving $curpkg here because calls to xdc.module end
* up calling xdc.loadPackage, where dependencies are recorded. If we
* didn't change the value in $curpkg when getting the reference to
* xdc.cfg.Main, we would create an invalid dependency between the package
* that called xdc.useModule(qname) and 'xdc.cfg'.
*
* Afterwards, when calling xdc.module(qname), we need to set $curpkg back
* to its initial value to correctly record the dependency between the
* initial $curpkg and qname's package.
*
* Finally, when calling $$usemod, $curpkg is set to qname's package
* because $$usemod may call qname.module$use. We could save $curpkg
* inside $$usemod but it might be better to keep this part of $curpkg
* handling here.
*
* The value of $curpkg is also changed in closePackages(), when calling
* module$use for modules used in the user's script phase, and in
* xdc.loadPackage() before loading schemas.
*/
_myTrace("xdc.useModule called on " + qname, 2, ["moduleUse"]);
var saveCurpkg = xdc.om.$curpkg;
xdc.om.$$bind('$curpkg', undefined);
var cfgModel = xdc.om['xdc.cfg.Main'];
/* This lets us call useModule with a Module as a parameter */
if (typeof qname != 'string') { /// TEMPORARY HACK
xdc.om.$$bind('$curpkg', qname.$orig.$package.$name);
var mod = cfgModel.$private.$$usemod(qname, cycles);
xdc.om.$$bind('$curpkg', saveCurpkg);
return (mod);
}
xdc.om.$$bind('$curpkg', saveCurpkg);
var unit = xdc.module(qname);
if (unit.$spec.isInter()) {
xdc.om.$$bind('$curpkg', saveCurpkg);
return (unit);
}
xdc.om.$$bind('$curpkg', unit.$orig.$package.$name);
cfgModel.$private.$$usemod(unit, cycles);
xdc.om.$$bind('$curpkg', saveCurpkg);
return unit;
}
/*
* ======== _myTrace ========
*/
function _myTrace(msg, level, groups)
{
groups.push("all");
utils._tracePrint("xdc: " + msg, "xdc/cfg/Main.xs", "xdc/cfg",
groups, level);
}