2553 lines
85 KiB
Haxe
Raw Normal View History

2025-01-22 16:18:30 +01:00
import CopyFile.Overwrite;
import haxe.io.Path;
import haxe.Json;
import sys.io.Process;
import sys.FileSystem;
#if haxe4
import sys.thread.Thread;
import sys.thread.Mutex;
import sys.thread.Tls;
#elseif neko
import neko.vm.Thread;
import neko.vm.Mutex;
import neko.vm.Tls;
#else
import cpp.vm.Thread;
import cpp.vm.Mutex;
import cpp.vm.Tls;
#end
import haxe.crypto.Md5;
import Log.NORMAL;
import Log.BOLD;
import Log.ITALIC;
import Log.YELLOW;
import Log.WHITE;
using StringTools;
#if (haxe_ver>=4)
typedef XmlAccess = haxe.xml.Access;
#else
typedef XmlAccess = haxe.xml.Fast;
#end
#if haxe3
typedef Hash<T> = haxe.ds.StringMap<T>;
#end
typedef FileGroups = Hash<FileGroup>;
typedef Targets = Hash<Target>;
typedef Prelinkers = Hash<Prelinker>;
typedef Linkers = Hash<Linker>;
class BuildTool
{
public inline static var SupportedVersion = 430;
var mDefines:Hash<String>;
var mCurrentIncludeFile:String;
var mIncludePath:Array<String>;
var mCompiler:Compiler;
var mStripper:Stripper;
var mManifester:Manifester;
var mPrelinkers:Prelinkers;
var mLinkers:Linkers;
var mCopyFiles:Array<CopyFile>;
var mFileGroups:FileGroups;
var mTargets:Targets;
var mFileStack:Array<String>;
var mMakefile:String;
var mMagicLibs:Array<{name:String, replace:String}>;
var mPragmaOnce:Map<String,Bool>;
var mNvccFlags:Array<String>;
var mNvccLinkFlags:Array<String>;
var mDirtyList:Array<String>;
var arm64:Bool;
var m64:Bool;
var m32:Bool;
public static var os="";
public static var sAllowNumProcs = true;
public static var sCompileThreadCount = 1;
public static var sThreadPool:ThreadPool;
public static var sReportedThreads = -1;
public static var HXCPP = "";
public static var is64 = false;
public static var isWindows = false;
public static var isWindowsArm = false;
public static var isLinux = false;
public static var isRPi = false;
public static var isMac = false;
public static var targetKey:String;
public static var instance:BuildTool;
public static var helperThread = new Tls<Thread>();
public static var destination:String;
public static var outputs = new Array<String>();
public static var groupMutex = new Mutex();
static var mVarMatch = new EReg("\\${(.*?)}","");
static var mNoDollarMatch = new EReg("{(.*?)}","");
public static var exitOnThreadError = false;
public static var threadExitCode = 0;
public static var startDir:String;
public function new(inJob:String,inDefines:Hash<String>,inTargets:Array<String>,
inIncludePath:Array<String>, inDirtyList:Array<String> )
{
mDefines = inDefines;
mFileGroups = new FileGroups();
mCompiler = null;
mStripper = null;
mTargets = new Targets();
mPrelinkers = new Prelinkers();
mLinkers = new Linkers();
mCurrentIncludeFile = "";
mFileStack = [];
mCopyFiles = [];
mIncludePath = inIncludePath;
mPragmaOnce = new Map<String,Bool>();
mMagicLibs = [];
mNvccFlags = [];
mNvccLinkFlags = [];
mMakefile = "";
mDirtyList = inDirtyList;
if (inJob=="cache")
{
}
else
{
mMakefile = inJob;
if (!PathManager.isAbsolute(mMakefile) && sys.FileSystem.exists(mMakefile))
mMakefile = sys.FileSystem.fullPath(mMakefile);
mDefines.set("HXCPP_BUILD_DIR", Path.addTrailingSlash(Path.directory(mMakefile)) );
}
instance = this;
m64 = mDefines.exists("HXCPP_M64");
m32 = mDefines.exists("HXCPP_M32");
arm64 = mDefines.exists("HXCPP_ARM64");
if (m64==m32 && !arm64)
{
var arch = getArch();
// Default to the current OS version. windowsArm runs m32 code too
m64 = arch=="m64";
m32 = arch=="m32";
arm64 = arch=="arm64";
mDefines.remove(m32 ? "HXCPP_M64" : "HXCPP_M32");
set64(mDefines,m64,arm64);
}
Profile.setEntry("parse xml");
include("toolchain/setup.xml");
if (mDefines.exists("toolchain"))
{
if (!mDefines.exists("BINDIR"))
{
mDefines.set("BINDIR", Path.withoutDirectory(Path.withoutExtension(mDefines.get("toolchain"))));
}
if ( (new EReg("window","i")).match(os) )
mDefines.set("windows_host","1");
}
else
setDefaultToolchain(mDefines);
if (mDefines.exists("dll_import"))
{
var path = new Path(mDefines.get("dll_import"));
if (!mDefines.exists("dll_import_include"))
mDefines.set("dll_import_include", path.dir + "/include" );
if (!mDefines.exists("dll_import_link"))
mDefines.set("dll_import_link", mDefines.get("dll_import") );
}
setupAppleDirectories(mDefines);
if (isMsvc())
{
mDefines.set("isMsvc","1");
if (Std.parseInt(mDefines.get("MSVC_VER"))>=18)
mDefines.set("MSVC18+","1");
}
include("toolchain/finish-setup.xml", false);
if (mMakefile!="")
{
pushFile(mMakefile,"makefile");
var make_contents = "";
try {
make_contents = sys.io.File.getContent(mMakefile);
} catch (e:Dynamic) {
Log.error("Could not open build file \"" + mMakefile + "\"");
//println("Could not open build file '" + mMakefile + "'");
//Sys.exit(1);
}
var xml_slow = Xml.parse(make_contents);
var xml = new XmlAccess(xml_slow.firstElement());
parseXML(xml,"",false);
popFile();
include("toolchain/" + mDefines.get("toolchain") + "-toolchain.xml", false);
if (mDefines.exists("HXCPP_CONFIG"))
include(mDefines.get("HXCPP_CONFIG"),"exes",true);
}
for(group in mFileGroups)
group.filter(mDefines);
if (Log.verbose) Log.println ("");
// MSVC needs this before the toolchain file, Emscripten wants to set HXCPP_COMPILE_THREADS
// If not already calculated in "setup"
getThreadCount();
var cached = CompileCache.init(mDefines);
Profile.setEntry("setup cache");
if (inJob=="cache")
{
if (!cached)
{
Log.error("HXCPP_COMPILE_CACHE is not set");
}
switch(inTargets[0])
{
case "days" :
var days = inTargets[1]==null ? null : Std.parseInt(inTargets[1]);
if (days==null)
{
Log.error("cache days - expected day count");
Tools.exit(1);
}
CompileCache.clear(days,0,true,null);
case "resize" :
var mb = inTargets[1]==null ? null : Std.parseInt(inTargets[1]);
if (mb==null)
{
Log.error("cache resize - expected megabyte count");
Tools.exit(1);
}
CompileCache.clear(0,mb,true,inTargets[2]);
case "clear" : CompileCache.clear(0,0,true,inTargets[1]);
case "list" : CompileCache.list(false,inTargets[1]);
case "details" : CompileCache.list(true,inTargets[1]);
default:
printUsage();
Tools.exit(1);
}
return;
}
if (cached)
{
var cacheSize = mDefines.exists("HXCPP_CACHE_MB") ? Std.parseInt( mDefines.get("HXCPP_CACHE_MB") ) : 1000;
if (cacheSize!=null && cacheSize>0)
CompileCache.clear(0,cacheSize,false,null);
}
if (Log.verbose) Log.println ("");
if (inTargets.remove("clear"))
{
Profile.setEntry("clear");
for(target in mTargets.keys())
cleanTarget(target,false);
}
if (inTargets.remove("clean"))
{
Profile.setEntry("clean");
for(target in mTargets.keys())
cleanTarget(target,true);
}
if (destination!=null && inTargets.length!=1)
{
Log.warn("Exactly one target must be specified with 'destination'. Specified:" + inTargets );
destination = null;
}
Profile.setEntry("build");
for(target in inTargets)
buildTarget(target,destination);
var linkOutputs = mDefines.get("HXCPP_LINK_OUTPUTS");
if (linkOutputs!=null)
sys.io.File.saveContent(linkOutputs,outputs.join("\n")+"\n");
if (Log.verbose)
{
for(out in outputs)
Log.v(" generated " + out);
}
if (threadExitCode != 0)
Tools.exit(threadExitCode);
}
public static function isDefault64()
{
}
public function pushFile(inFilename:String, inWhy:String, inSection:String="")
{
Log.info("", " - \x1b[1mParsing " + inWhy + ":\x1b[0m " + inFilename + (inSection == "" ? "" : " \x1b[3m(section \"" + inSection + "\")\x1b[0m"));
mFileStack.push(inFilename);
}
public function popFile()
{
mFileStack.pop();
}
public static function addOutput(inWhat:String, inWhere:String)
{
outputs.push(inWhat + "=" + inWhere);
}
public static function getThreadCount() : Int
{
if (instance==null)
return sCompileThreadCount;
var defs = instance.mDefines;
if (sAllowNumProcs)
{
var thread_var = defs.exists("HXCPP_COMPILE_THREADS") ?
defs.get("HXCPP_COMPILE_THREADS") : Sys.getEnv("HXCPP_COMPILE_THREADS");
if (thread_var == null)
{
sCompileThreadCount = getNumberOfProcesses();
}
else
{
sCompileThreadCount = (Std.parseInt(thread_var)<2) ? 1 : Std.parseInt(thread_var);
}
if (sCompileThreadCount!=sReportedThreads)
{
sReportedThreads = sCompileThreadCount;
Log.v("\x1b[33;1mUsing compile threads: " + sCompileThreadCount + "\x1b[0m");
}
}
if (sCompileThreadCount>1 && sThreadPool==null)
sThreadPool = new ThreadPool(sCompileThreadCount);
return sCompileThreadCount;
}
public static function setThreadError(inCode:Int)
{
threadExitCode = inCode;
if (exitOnThreadError)
Tools.exit(inCode);
}
public function buildTarget(inTarget:String, inDestination:String)
{
//var dependDebug = function(s:String) Log.error(s);
var dependDebug = null;
// Sys.println("Build : " + inTarget );
if (!mTargets.exists(inTarget))
{
Log.error ("Could not find build target \"" + inTarget + "\"");
//throw "Could not find target '" + inTarget + "' to build.";
}
if (mCompiler==null)
{
Log.error("No compiler defined for the current build target");
//throw "No compiler defined";
}
var target:Target = mTargets.get(inTarget);
target.checkError();
for(sub in target.mSubTargets)
buildTarget(sub,null);
var threadPool = BuildTool.sThreadPool;
PathManager.resetDirectoryCache();
var restoreDir = "";
if (target.mBuildDir!="")
{
restoreDir = Sys.getCwd();
Log.info("", " - \x1b[1mChanging directory:\x1b[0m " + target.mBuildDir);
Sys.setCwd(target.mBuildDir);
}
targetKey = inTarget + target.getKey();
var objs = new Array<String>();
mCompiler.objToAbsolute();
if (target.mFileGroups.length > 0)
PathManager.mkdir(mCompiler.mObjDir);
var baseDir = Sys.getCwd();
for(group in target.mFileGroups)
{
var useCache = CompileCache.hasCache && group.mUseCache;
if (!useCache && group.mUseCache)
Log.v("Ignoring compiler cache because HXCPP_COMPILE_CACHE is not valid.");
var groupObjs = new Array<String>();
if (group.mDir!="." && group.mSetImportDir)
Sys.setCwd( PathManager.combine(baseDir, group.mDir ) );
group.checkOptions(mCompiler.mObjDir);
group.checkDependsExist();
if (!mCompiler.initPrecompile(mDefines.get("USE_PRECOMPILED_HEADERS") ))
group.dontPrecompile();
group.preBuild();
var to_be_compiled = new Array<File>();
var cached = useCache && mCompiler.createCompilerVersion(group);
var inList = new Array<Bool>();
var groupIsOutOfDate = mDirtyList.indexOf(group.mId)>=0 || mDirtyList.indexOf("all")>=0;
if (useCache)
{
Profile.push("compute hash");
if (useCache && group.hasFiles() && threadPool!=null)
{
Log.initMultiThreaded();
var names:Array<String> = Lambda.array(Lambda.map(group.mFiles, function(file:File) {return file.mName; }));
threadPool.setArrayCount( names.length );
threadPool.runJob( function(tid) {
var localCache = new Map<String,String>();
while(threadExitCode==0)
{
var id = sThreadPool.getNextIndex();
if (id<0)
break;
group.mFiles.get(names[id]).computeDependHash(localCache);
}
} );
}
else
{
for(file in group.mFiles)
file.computeDependHash(null);
}
Profile.pop();
}
for(file in group.mFiles)
{
var obj_name = mCompiler.getCachedObjName(file);
groupObjs.push(obj_name);
var outOfDate = groupIsOutOfDate || file.isOutOfDate(obj_name, dependDebug);
if (outOfDate)
{
if (dependDebug!=null)
dependDebug(mCompiler.getCacheString(file));
to_be_compiled.push(file);
}
inList.push(outOfDate);
}
var someCompiled = to_be_compiled.length > 0;
var pchStamp:Null<Float> = null;
if (group.mPrecompiledHeader!="")
{
Profile.push("pch");
var obj = mCompiler.precompile(group,cached || to_be_compiled.length==0);
if (obj!=null)
{
pchStamp = FileSystem.stat(obj).mtime.getTime();
groupObjs.push(obj);
/*
for(i in 0...group.mFiles.length)
{
var obj_name = groupObjs[i];
if (!inList[i])
{
if (FileSystem.stat(obj_name).mtime.getTime() < pchStamp)
{
groupObjs.push(obj_name);
trace(' Add $obj_name');
}
else
trace(' Ok $obj_name');
}
else
{
trace(' Listed $obj_name ' + group.mFiles[i].mName);
}
}
*/
}
Profile.pop();
}
if (group.mConfig!="")
{
var lines = ["#ifndef HXCPP_CONFIG_INCLUDED","#define HXCPP_CONFIG_INCLUDED"];
var flags = group.mCompilerFlags.concat(mCompiler.getCompilerDefines("haxe"));
var define = ~/^-D([^=]*)=?(.*)/;
for(flag in flags)
{
if (define.match(flag))
{
var name = define.matched(1);
var val = define.matched(2);
lines.push("");
lines.push( '#if !defined($name) && !defined(NO_$name)' );
lines.push( '#define $name $val' );
lines.push( '#endif' );
}
}
lines.push("");
lines.push("#include <hxcpp.h>");
lines.push("");
lines.push("#endif");
lines.push("");
var filename = mDefines.exists("HXCPP_OUTPUT_CONFIG_NAME") ?
mDefines.get("HXCPP_OUTPUT_CONFIG_NAME") :
PathManager.combine( target.mOutputDir, group.mConfig );
if (!PathManager.isAbsolute(filename))
filename = PathManager.combine( Sys.getCwd(), filename);
var content = lines.join("\n");
if (!FileSystem.exists(filename) || sys.io.File.getContent(filename)!=content)
sys.io.File.saveContent(filename, content);
addOutput("config",filename);
}
var nvcc = group.mNvcc;
var first = true;
var groupHeader = (!Log.quiet && !Log.verbose) ? function()
{
if (first)
{
groupMutex.acquire();
if (first)
{
first = false;
Log.lock();
Log.println("");
Log.info("\x1b[33;1mCompiling group: " + group.mId + "\x1b[0m");
var message = "\x1b[1m" + (nvcc ? getNvcc() : mCompiler.mExe) + "\x1b[0m";
var flags = group.mCompilerFlags;
if (!nvcc)
flags = flags.concat(mCompiler.getFlagStrings());
else
flags = flags.concat( BuildTool.getNvccFlags() );
for (compilerFlag in flags)
{
if (StringTools.startsWith(compilerFlag, "-D"))
{
var index = compilerFlag.indexOf("(");
if (index > -1)
{
message += " \x1b[1m" + compilerFlag.substr(0, index) + "\x1b[0m\x1b[2m" + compilerFlag.substr(index) + "\x1b[0m";
}
else
{
message += " \x1b[1m" + compilerFlag + "\x1b[0m";
}
}
else
{
message += " \x1b[0m" + compilerFlag + "\x1b[0m";
}
}
message += " \x1b[2m...\x1b[0m \x1b[2mtags=" + group.mTags.split(",") + "\x1b[0m";
Log.info(message);
Log.unlock();
}
groupMutex.release();
}
} : null;
Profile.push("compile");
if (threadPool==null)
{
for(file in to_be_compiled)
mCompiler.compile(file,-1,groupHeader,pchStamp);
}
else
{
Log.initMultiThreaded();
var mutex = threadPool.mutex;
var compiler = mCompiler;
threadPool.setArrayCount(to_be_compiled.length);
threadPool.runJob( function(threadId:Int) {
while(threadExitCode==0)
{
var index = threadPool.getNextIndex();
if (index<0)
break;
var file = to_be_compiled[index];
compiler.compile(file,threadId,groupHeader,pchStamp);
}
});
}
Profile.pop();
if (CompileCache.hasCache && group.mAsLibrary && mLinkers.exists("static_link"))
{
Profile.push("link libs");
var linker = mLinkers.get("static_link");
var targetDir = mCompiler.mObjDir;
if (useCache)
{
targetDir = CompileCache.compileCache + "/" + group.getCacheProject() + "/lib";
PathManager.mkdir(targetDir);
}
var libName = targetDir + "/" + mCompiler.getTargetPrefix() + "_" + group.getCacheProject();
var libTarget = new Target(libName, "linker", "static_link" );
linker.link(libTarget,groupObjs, mCompiler, [] );
target.mAutoLibs.push(linker.mLastOutName);
// Linux the libraries must be added again if the references were not resolved the firs time
if (group.mAddTwice)
target.mLibs.push(linker.mLastOutName);
Profile.pop();
}
else if (nvcc)
{
var objDir = mCompiler.mObjDir;
if (group.isCached())
objDir = CompileCache.compileCache;
var extraObj = linkNvccFiles(objDir, someCompiled, groupObjs, group.mId, mCompiler.mExt);
groupObjs.push(extraObj);
objs = objs.concat(groupObjs);
}
else
{
objs = objs.concat(groupObjs);
}
if (group.mDir!="." && group.mSetImportDir)
Sys.setCwd( baseDir );
}
switch(target.mTool)
{
case "linker":
Profile.push("linker");
if (mPrelinkers.exists(target.mToolID))
{
var result = mPrelinkers.get(target.mToolID).prelink(target,objs, mCompiler);
if (result != "")
objs.push(result);
//throw "Missing linker :\"" + target.mToolID + "\"";
}
if (!mLinkers.exists(target.mToolID))
{
Log.error ("Could not find linker for \"" + target.mToolID + "\"");
//throw "Missing linker :\"" + target.mToolID + "\"";
}
var extraDeps = [];
var manifest = mDefines.get("manifestFile");
if (manifest!=null)
extraDeps.push(manifest);
var linker = mLinkers.get(target.mToolID);
var output = linker.link(target,objs, mCompiler, extraDeps);
if (output!="")
{
if (mStripper!=null)
{
if (target.mToolID=="exe" || target.mToolID=="dll")
{
if ( mDefines.exists("HXCPP_DEBUG_LINK_AND_STRIP") )
{
var unstripped = linker.getUnstrippedFilename(mCompiler.mObjDir,target);
//var unstripped = mCompiler.mObjDir + "/" + linker.getSimpleFilename(target);
Log.v("Save unstripped to " + unstripped);
var chmod = isWindows ? false : target.mToolID=="exe";
CopyFile.copyFile(output, unstripped, false, Overwrite.ALWAYS, chmod);
}
mStripper.strip(output);
}
}
if (manifest!=null && (target.mToolID=="exe" || target.mToolID=="dll") )
{
if (mManifester==null)
{
Log.v('Could not find manifest tool for "$manifest" - ignoring');
}
else
{
//if (!PathManager.isAbsolute(manifest))
//manifest = PathManager.combine(startDir,manifest);
Log.v('Adding manifest "$manifest"');
mManifester.add(output, manifest,target.mToolID=="exe");
}
}
}
var outFile = linker.mLastOutName;
if (outFile!="" && !PathManager.isAbsolute(outFile) && sys.FileSystem.exists(mMakefile))
{
var baseFile = PathManager.standardize(mMakefile);
var parts = baseFile.split("/");
parts[ parts.length-1 ] = outFile;
outFile = parts.join("/");
}
if (outFile!="")
addOutput(target.mToolID, outFile);
if (output!="" && inDestination!=null)
{
inDestination = substitute(inDestination,false);
if (inDestination!="")
{
if (!PathManager.isAbsolute(inDestination) && sys.FileSystem.exists(mMakefile))
{
var baseFile = PathManager.standardize(mMakefile);
var parts = baseFile.split("/");
parts[ parts.length-1 ] = inDestination;
inDestination = parts.join("/");
}
inDestination = PathManager.clean(inDestination);
var fileParts = inDestination.split("/");
fileParts.pop();
PathManager.mkdir(fileParts.join("/"));
var chmod = isWindows ? false : target.mToolID=="exe";
CopyFile.copyFile(output, inDestination, false, Overwrite.ALWAYS, chmod);
}
}
Profile.pop();
}
if (mCopyFiles.length>0)
{
Profile.push("copy files");
for(copyFile in mCopyFiles)
if (copyFile.toolId==null || copyFile.toolId==target.mToolID)
copyFile.copy(target.mOutputDir);
Profile.pop();
}
if (restoreDir!="")
Sys.setCwd(restoreDir);
}
function linkNvccFiles(objDir:String, hasChanged:Bool, nvObjs:Array<String>, inGroupName:String, objExt:String)
{
// nvcc -arch=sm_30 -dlink test1.o test2.o -o link.o
// Sadly, nvcc has no 'fromFile' options, so we must do it from objDir
var objDirLen = objDir.length;
var last = objDir.substr(objDirLen-1);
if (last!="/" && last!="\\")
objDirLen++;
var outFile = "nvcc_" + inGroupName + mCompiler.mExt;
var fullFile = objDir + "/" + outFile;
if (hasChanged || !sys.FileSystem.exists(fullFile) )
{
var maxObjs = 25;
var shortObjs = nvObjs.map( function(f) return f.substr(objDirLen) );
if (shortObjs.length>maxObjs)
{
var partObjs = new Array<String>();
var p0 = 0;
var n =shortObjs.length;
var groupIdx = 0;
while(p0<n)
{
var subName = "nvcc_" + inGroupName + "_" + (groupIdx++) + mCompiler.mExt;
var remain = n-p0;
var use = remain<maxObjs ? remain : remain<maxObjs*2 ? (remain>>1) : maxObjs;
var files = shortObjs.slice(p0,p0+use);
var flags = getNvccLinkFlags().concat(files).concat(["-o",subName]);
var dbgFlags = getNvccLinkFlags().concat(["[.", "x"+subName.length,".]"]).concat(["-o",subName]);
Log.v("Linking nvcc in " + objDir + ":" + getNvcc() + dbgFlags.join(" ") );
ProcessManager.runCommand(objDir ,getNvcc(), flags );
partObjs.push(subName);
p0 += use;
}
shortObjs = partObjs;
}
var flags = getNvccLinkFlags().concat(shortObjs).concat(["-o",outFile]);
var dbgFlags = getNvccLinkFlags().concat(["[.", "x"+shortObjs.length,".]"]).concat(["-o",outFile]);
Log.v("Linking nvcc in " + objDir + ":" + getNvcc() + dbgFlags.join(" ") );
ProcessManager.runCommand(objDir ,getNvcc(), flags );
}
return fullFile;
}
public function cleanTarget(inTarget:String,allObj:Bool)
{
// Sys.println("Build : " + inTarget );
if (!mTargets.exists(inTarget))
{
Log.error("Could not find build target \"" + inTarget + "\"");
//throw "Could not find target '" + inTarget + "' to build.";
}
if (mCompiler==null)
{
Log.error("No compiler defined");
//throw "No compiler defined";
}
var target = mTargets.get(inTarget);
target.checkError();
for(sub in target.mSubTargets)
cleanTarget(sub,allObj);
var restoreDir = "";
if (target.mBuildDir!="")
{
restoreDir = Sys.getCwd();
Log.info("", " - \x1b[1mChanging directory:\x1b[0m " + target.mBuildDir);
Sys.setCwd(target.mBuildDir);
}
PathManager.removeDirectory(mCompiler.mObjDir);
PathManager.removeFile("all_objs");
PathManager.removeFilesWithExtension(".pdb");
if (allObj)
PathManager.removeDirectory("obj");
if (restoreDir!="")
Sys.setCwd(restoreDir);
}
public function createCompiler(inXML:XmlAccess,inBase:Compiler) : Compiler
{
var c = inBase;
var id = inXML.has.id ? substitute(inXML.att.id) : null;
var exe = inXML.has.exe ? substitute(inXML.att.exe) : null;
if (inBase==null || inXML.has.replace)
{
c = new Compiler(id,exe);
}
else
{
if (id!=null)
c.mID = id;
if (exe!=null)
c.mExe = exe;
}
c.mAddGCCIdentity = mDefines.exists("USE_GCC_FILETYPES");
for(el in inXML.elements)
{
if (valid(el,""))
switch(el.name)
{
case "flag" : c.addFlag(substitute(el.att.value), el.has.tag?substitute(el.att.tag):"");
case "cflag" : c.mCFlags.push(substitute(el.att.value));
case "cppflag" : c.mCPPFlags.push(substitute(el.att.value));
case "objcflag" : c.mOBJCFlags.push(substitute(el.att.value));
case "rcflag" : c.mRcFlags.push( substitute((el.att.value)) );
case "mmflag" : c.mMMFlags.push(substitute(el.att.value));
case "pchflag" : c.mPCHFlags.push(substitute(el.att.value));
case "objdir" : c.mObjDir = substitute((el.att.value));
case "outflag" : c.mOutFlag = substitute((el.att.value));
case "exe" : c.mExe = substitute((el.att.name));
case "rcexe" : c.mRcExe = substitute((el.att.name));
case "rcext" : c.mRcExt = substitute((el.att.value));
case "ext" : c.mExt = substitute((el.att.value));
case "pch" : c.setPCH( substitute((el.att.value)) );
case "getversion" : c.mGetCompilerVersion = substitute((el.att.value));
case "section" : createCompiler(el,c);
case "include" :
var name = substitute(el.att.name);
var full_name = findIncludeFile(name);
if (full_name!="")
{
pushFile(full_name,"compiler");
var make_contents = sys.io.File.getContent(full_name);
var xml_slow = Xml.parse(make_contents);
createCompiler(new XmlAccess(xml_slow.firstElement()),c);
popFile();
}
else if (!el.has.noerror)
{
Log.error("Could not find include file \"" + name + "\"");
//throw "Could not find include file " + name;
}
default:
Log.error("Unknown compiler option \"" + el.name + "\"");
//throw "Unknown compiler option: '" + el.name + "'";
}
}
return c;
}
public function loadNvccXml()
{
var incName = findIncludeFile("nvcc-setup.xml");
if (incName=="")
incName = findIncludeFile('$HXCPP/toolchain/nvcc-setup.xml');
if (incName=="")
Log.error("Could not setup nvcc - missing nvcc-setup.xml");
else if (!mPragmaOnce.get(incName))
{
pushFile(incName, "Nvcc");
var make_contents = sys.io.File.getContent(incName);
mPragmaOnce.set(incName,true);
var xml = Xml.parse(make_contents);
parseXML(new XmlAccess(xml.firstElement()),"", false);
popFile();
}
}
public static function setupNvcc()
{
instance.loadNvccXml();
}
public function createFileGroup(inXML:XmlAccess,inFiles:FileGroup,inName:String, inForceRelative:Bool, inTags:String):FileGroup
{
var dir = inXML.has.dir ? substitute(inXML.att.dir) : ".";
if (inForceRelative)
dir = PathManager.combine( Path.directory(mCurrentIncludeFile), dir );
var group:FileGroup = inFiles==null ? new FileGroup(dir,inName, inForceRelative) :
inXML.has.replace ? inFiles.replace(dir, inForceRelative) :
inFiles;
if (inTags!=null)
group.mTags = inTags;
for(el in inXML.elements)
{
if (valid(el,""))
switch(el.name)
{
case "file" :
var name = substitute(el.att.name);
var file:File = group.find(name);
if (file==null)
{
file = new File(name,group);
group.addFile( file );
}
if (el.has.tag)
{
var extra = substitute(el.att.tag);
file.setTags(group.mTags==null || group.mTags=="" ? extra : group.mTags+","+extra);
}
if (el.has.tags)
file.setTags( substitute(el.att.tags) );
if (el.has.filterout)
file.mFilterOut = substitute(el.att.filterout);
if (el.has.embedName)
file.mEmbedName = substitute(el.att.embedName);
if (el.has.scramble)
file.mScramble = substitute(el.att.scramble);
for(f in el.elements)
if (valid(f,"") && f.name=="depend")
file.mDepends.push( substitute(f.att.name) );
case "section" : createFileGroup(el,group,inName,inForceRelative,null);
case "cache" :
group.mUseCache = parseBool( substitute(el.att.value) );
if (el.has.project)
group.mCacheProject = substitute(el.att.project);
if (el.has.asLibrary)
group.mAsLibrary = true;
if (el.has.respectTimestamp)
group.mRespectTimestamp = true;
case "tag" :
group.addTag( substitute(el.att.value) );
case "addTwice" :
group.mAddTwice = true;
case "depend" :
if (el.has.name)
{
var dateOnly = el.has.dateOnly && parseBool( substitute(el.att.dateOnly) );
group.addDepend( substitute(el.att.name), dateOnly );
}
else if (el.has.files)
{
var name = substitute(el.att.files);
if (!mFileGroups.exists(name))
Log.error( "Could not find filegroup for depend node:" + name );
group.addDependFiles(mFileGroups.get(name));
}
else
Log.error("depend node must have 'name' or 'files' attribute");
case "hlsl" :
group.addHLSL( substitute(el.att.name), substitute(el.att.profile),
substitute(el.att.variable), substitute(el.att.target) );
case "options" : group.addOptions( substitute(el.att.name) );
case "config" : group.mConfig = substitute(el.att.name);
case "compilerflag" :
if (el.has.name)
group.addCompilerFlag( substitute(el.att.name) );
group.addCompilerFlag( substitute(el.att.value) );
case "nvcc" :
setupNvcc();
group.mNvcc = true;
if (group.mTags=="haxe,static")
group.mTags=="nvcc";
case "objprefix" :
group.mObjPrefix = substitute(el.att.value);
case "compilervalue" :
group.addCompilerFlag( substitute(el.att.name) );
group.addCompilerFlag( substitute(el.att.value) );
case "precompiledheader" :
group.setPrecompiled( substitute(el.att.name), substitute(el.att.dir) );
case "include" :
var subbed_name = substitute(el.att.name);
var full_name = findIncludeFile(subbed_name);
if (full_name!="")
{
if (!mPragmaOnce.get(full_name))
{
pushFile(full_name, "FileGroup");
var make_contents = sys.io.File.getContent(full_name);
var xml_slow = Xml.parse(make_contents);
createFileGroup(new XmlAccess(xml_slow.firstElement()), group, inName, false,null);
popFile();
}
}
else
{
Log.error("Could not find include file \"" + subbed_name + "\"");
}
}
}
return group;
}
public function createLinker(inXML:XmlAccess,inBase:Linker):Linker
{
var exe:String = inXML.has.exe ? substitute(inXML.att.exe) : null;
if (inBase!=null && !inXML.has.replace && inBase.mExe==null)
inBase.mExe = exe;
var l = (inBase!=null && !inXML.has.replace) ? inBase : new Linker(exe);
for(el in inXML.elements)
{
if (valid(el,""))
switch(el.name)
{
case "flag" : l.mFlags.push(substitute(el.att.value));
case "ext" : l.mExt = (substitute(el.att.value));
case "outflag" : l.mOutFlag = (substitute(el.att.value));
case "libdir" : l.mLibDir = (substitute(el.att.name));
case "lib" :
if (el.has.hxbase)
l.mLibs.push( substitute(el.att.hxbase) + mDefines.get("LIBEXTRA") + mDefines.get("LIBEXT") );
else if (el.has.base)
l.mLibs.push( substitute(el.att.base) + mDefines.get("LIBEXT") );
else
l.mLibs.push( substitute(el.att.name) );
case "prefix" : l.mNamePrefix = substitute(el.att.value);
case "ranlib" : l.mRanLib = (substitute(el.att.name));
case "libpathflag" : l.mAddLibPath = (substitute(el.att.value));
case "recreate" : l.mRecreate = (substitute(el.att.value)) != "";
case "expandAr" : l.mExpandArchives = substitute(el.att.value) != "";
case "fromfile" :
if (el.has.value)
l.mFromFile = substitute(el.att.value);
if (el.has.needsQuotes)
l.mFromFileNeedsQuotes = parseBool(substitute(el.att.needsQuotes));
case "exe" : l.mExe = (substitute(el.att.name));
case "section" : createLinker(el,l);
}
}
return l;
}
public function createPrelinker(inXML:XmlAccess,inBase:Prelinker):Prelinker
{
var l = (inBase!=null && !inXML.has.replace) ? inBase : new Prelinker(substitute(inXML.att.exe));
for(el in inXML.elements)
{
if (valid(el,""))
switch(el.name)
{
case "flag" : l.mFlags.push(substitute(el.att.value));
//case "ext" : l.mExt = (substitute(el.att.value));
case "outflag" : l.mOutFlag = (substitute(el.att.value));
case "expandAr" : l.mExpandArchives = substitute(el.att.value) != "";
case "fromfile" : l.mFromFile = (substitute(el.att.value));
case "exe" : l.mExe = (substitute(el.att.name));
case "section" : createPrelinker(el,l);
}
}
return l;
}
public function createManifester(inXML:XmlAccess,inBase:Manifester):Manifester
{
var s = (inBase!=null && !inXML.has.replace) ? inBase :
new Manifester(substitute(inXML.att.exe));
for(el in inXML.elements)
{
if (valid(el,""))
switch(el.name)
{
case "flag" : s.mFlags.push(substitute(el.att.value));
case "outPre" : s.mOutPre = substitute(el.att.value);
case "outPost" : s.mOutPost = substitute(el.att.value);
case "exe" : s.mExe = substitute((el.att.name));
}
}
return s;
}
public function createStripper(inXML:XmlAccess,inBase:Stripper):Stripper
{
var s = (inBase!=null && !inXML.has.replace) ? inBase :
new Stripper(substitute(inXML.att.exe));
for(el in inXML.elements)
{
if (valid(el,""))
switch(el.name)
{
case "flag" : s.mFlags.push(substitute(el.att.value));
case "exe" : s.mExe = substitute((el.att.name));
}
}
return s;
}
public function createTarget(inXML:XmlAccess,?inTarget:Target, inForceRelative) : Target
{
var target:Target = inTarget;
var output = inXML.has.output ? substitute(inXML.att.output) : "";
var tool = inXML.has.tool ? substitute(inXML.att.tool) : "";
var toolid = inXML.has.toolid ? substitute(inXML.att.toolid) : "";
if (target==null)
{
target = new Target(output,tool,toolid);
if (inForceRelative)
target.mBuildDir = Path.directory(mCurrentIncludeFile);
}
else
{
if (output!="")
target.mOutput = output;
if (tool!="")
target.mTool = tool;
if (toolid!="")
target.mToolID = toolid;
}
for(el in inXML.elements)
{
if (valid(el,""))
switch(el.name)
{
case "target" : target.mSubTargets.push( substitute(el.att.id) );
case "merge" :
var name = substitute(el.att.id);
if (!mTargets.exists(name))
Log.error("Could not find target " + name + " to merge.");
target.merge( mTargets.get(name) );
case "lib" :
if (el.has.hxbase)
target.mLibs.push( substitute(el.att.hxbase) + mDefines.get("LIBEXTRA") + mDefines.get("LIBEXT") );
else if (el.has.base)
target.mLibs.push( substitute(el.att.base) + mDefines.get("LIBEXT") );
else
{
var lib = substitute(el.att.name);
var found = false;
for(magicLib in mMagicLibs)
{
if (lib.endsWith(magicLib.name))
{
var replace = lib.substr(0, lib.length-magicLib.name.length) +
magicLib.replace;
Log.v('Using $replace instead of $lib');
found = true;
include(replace, "", false, true );
break;
}
}
if (!found)
target.mLibs.push(lib);
}
case "flag" : target.mFlags.push( substitute(el.att.value) );
case "depend" : target.mDepends.push( substitute(el.att.name) );
case "vflag" :
target.mFlags.push( substitute(el.att.name) );
target.mFlags.push( substitute(el.att.value) );
case "dir" : target.mDirs.push( substitute(el.att.name) );
case "outdir" : target.mOutputDir = substitute(el.att.name)+"/";
case "ext" : target.setExt( (substitute(el.att.value)) );
case "builddir" : target.mBuildDir = substitute(el.att.name);
case "libpath" : target.mLibPaths.push( substitute(el.att.name) );
case "fullouput" : target.mFullOutputName = substitute(el.att.name);
case "fullunstripped" : target.mFullUnstrippedName = substitute(el.att.name);
case "files" :
var id = el.att.id;
if (!mFileGroups.exists(id))
target.addError( "Could not find filegroup " + id );
else
target.addFiles( mFileGroups.get(id), el.has.asLibrary );
case "section" : createTarget(el,target,false);
}
}
return target;
}
public function defined(inString:String):Bool
{
if (inString=="this_dir")
return true;
return mDefines.exists(inString);
}
public function parseBool(inValue:String):Bool
{
return inValue=="1" || inValue=="t" || inValue=="true";
}
function findLocalIncludeFile(inBase:String):String
{
if (inBase == null || inBase=="") return "";
var c0 = inBase.substr(0,1);
if (c0!="/" && c0!="\\")
{
var c1 = inBase.substr(1,1);
if (c1!=":")
{
if (mCurrentIncludeFile!="")
{
var relative = Path.directory(mCurrentIncludeFile);
var name = PathManager.combine(relative, inBase);
if (FileSystem.exists(name))
return name;
}
for(p in mIncludePath)
{
var name = PathManager.combine(p, inBase);
if (FileSystem.exists(name))
{
return name;
}
}
return "";
}
}
if (FileSystem.exists(inBase))
return inBase;
return "";
}
function findIncludeFile(inBase:String):String
{
var result = findLocalIncludeFile(inBase);
if (result!="" && !Path.isAbsolute(result))
result = Path.normalize( PathManager.combine( mCurrentIncludeFile=="" ? Sys.getCwd() : Path.directory(mCurrentIncludeFile), result ) );
return result;
}
private static function getArch():String
{
if (isWindows)
{
if (isWindowsArm)
return "arm64";
var architecture = Sys.getEnv("PROCESSOR_ARCHITECTURE");
var wow64Architecture = Sys.getEnv("PROCESSOR_ARCHITEW6432");
if (architecture.indexOf("64") > -1 || wow64Architecture != null && wow64Architecture.indexOf("64") > -1)
{
return "m64";
}
else
{
return "m32";
}
}
else
{
var process = new Process("uname", [ "-m" ]);
var output = process.stdout.readAll().toString();
var error = process.stderr.readAll().toString();
process.exitCode();
process.close();
if ( (output.indexOf("aarch64") > -1) || (output.indexOf("arm64") > -1) )
{
return "arm64";
}
else if (output.indexOf("64") > -1)
{
return "m64";
}
else
{
return "m32";
}
}
}
static public function getMsvcVer()
{
return instance.mDefines.get("MSVC_VER");
}
static public function keepTemp()
{
return instance.mDefines.exists("HXCPP_KEEP_TEMP");
}
// Setting HXCPP_COMPILE_THREADS to 2x number or cores can help with hyperthreading
public static function getNumberOfProcesses():Int
{
var cache = Log.verbose;
Log.verbose = false;
var result = null;
if (isWindows)
{
var env = Sys.getEnv("NUMBER_OF_PROCESSORS");
if (env != null)
{
result = env;
}
}
else if (isLinux)
{
result = ProcessManager.runProcessLine("", "nproc", [], true, false, true, true);
if (result == null)
{
var cpuinfo = ProcessManager.runProcess("", "cat", [ "/proc/cpuinfo" ], true, false, true, true);
if (cpuinfo != null)
{
var split = cpuinfo.split("processor");
result = Std.string(split.length - 1);
}
}
}
else if (isMac)
{
var cores = ~/Total Number of Cores: (\d+)/;
var output = ProcessManager.runProcess("", "/usr/sbin/system_profiler", [ "-detailLevel", "full", "SPHardwareDataType" ], true, false, true, true);
if (output != null && cores.match(output))
{
result = cores.matched(1);
}
}
Log.verbose = cache;
if (result == null || Std.parseInt(result) < 1)
{
return 1;
}
else
{
return Std.parseInt(result);
}
}
private static function getVersion():String
{
try
{
var json = Json.parse (sys.io.File.getContent (PathManager.getHaxelib ("hxcpp") + "/haxelib.json"));
return json.version;
}
catch (e:Dynamic)
{
return "0.0.0";
}
}
public static function isMsvc()
{
return instance.mDefines.get("toolchain")=="msvc";
}
public static function isMingw()
{
return instance.mDefines.get("toolchain")=="mingw";
}
public static function getNvcc()
{
return instance.mDefines.get("NVCC");
}
public static function getNvccLinkFlags() : Array<String>
{
return instance.mNvccLinkFlags;
}
public static function getNvccFlags() : Array<String>
{
return instance.mNvccFlags;
}
public static function copy(from:String, to:String)
{
Log.v('copy $from $to');
try {
if (FileSystem.isDirectory(to))
to += "/" + Path.withoutDirectory(from);
var bytes = sys.io.File.getBytes(from);
sys.io.File.saveBytes(to,bytes);
} catch(e:Dynamic)
{
Log.error('Could not copy file $from $to');
}
}
static public function main()
{
try {
runMain();
}
catch(e:Dynamic) {
Log.error('Uncaught exception on main thread: $e\n${haxe.CallStack.toString(haxe.CallStack.exceptionStack())}');
Tools.exit(1);
}
Tools.exit(0);
}
// Process args and environment.
static function runMain()
{
var targets = new Array<String>();
var defines = new Hash<String>();
var include_path = new Array<String>();
var makefile:String="";
var optionsTxt = "Options.txt";
Profile.start();
include_path.push(".");
var args = Sys.args();
var env = Sys.environment();
for(e in env.keys())
defines.set(e, Sys.getEnv(e) );
// Check for calling from haxelib ...
if (args.length>0)
{
var last:String = (new Path(args[args.length-1])).toString();
var isRootDir = last=="/";
if (!isRootDir)
{
var slash = last.substr(-1);
if (slash=="/"|| slash=="\\")
last = last.substr(0,last.length-1);
}
if (isRootDir || (FileSystem.exists(last) && FileSystem.isDirectory(last)))
{
// When called from haxelib, the last arg is the original directory, and
// the current direcory is the library directory.
HXCPP = PathManager.standardize(Sys.getCwd());
defines.set("HXCPP",HXCPP);
args.pop();
Sys.setCwd(last);
}
}
if (defines.exists("HXCPP_NO_COLOUR") || defines.exists("HXCPP_NO_COLOR"))
Log.colorSupported = false;
Log.verbose = defines.exists("HXCPP_VERBOSE");
exitOnThreadError = defines.exists("HXCPP_EXIT_ON_ERROR");
os = Sys.systemName();
startDir = Sys.getCwd();
isWindows = (new EReg("window","i")).match(os);
if (isWindows)
{
var proc = Sys.getEnv("PROCESSOR_IDENTIFIER");
isWindowsArm = proc!=null && (new EReg("\\barm","i")).match(proc);
if (isWindowsArm)
defines.set("windows_arm_host", "1");
}
isMac = (new EReg("mac","i")).match(os);
if (isMac)
defines.set("mac_host", "1");
isLinux = (new EReg("linux","i")).match(os);
if (isLinux)
defines.set("linux_host", "1");
if (args.length>0 && args[0].endsWith(".cppia"))
{
var binDir = isWindows ? "Windows" : isMac ? "Mac64" : isLinux ? "Linux64" : null;
if (binDir==null)
Log.error("Cppia is not supported on this host.");
var arch = getArch();
var binDir = isWindows ? (isWindowsArm ? "WindowsArm64" : "Windows64" ) :
isMac ? "Mac64" :
isLinux ? ("Linux64") :
null;
var exe = '$HXCPP/bin/$binDir/Cppia' + (isWindows ? ".exe" : "");
if (!isWindows)
{
var phase = "find";
try
{
var stat = FileSystem.stat(exe);
if (stat==null)
throw "Could not find exe:" + exe;
var mode = stat.mode;
var exeFlags = (1<<0) | (1<<3) | (1<<6);
if ( (mode&exeFlags) != exeFlags )
{
var phase = "add exe permissions to";
if (Sys.command( "chmod", ["755", exe])!=0)
Log.error('Please use root access to add execute permissions to $exe');
}
}
catch(e:Dynamic)
{
Log.error('Could not $phase Cppia host $exe ($e)');
}
}
#if (haxe_ver < 3.3)
// avoid issue of path with spaces
// https://github.com/HaxeFoundation/haxe/issues/3603
if (isWindows)
exe = '"$exe"';
#end
Tools.exit( Sys.command( exe, args ) );
}
else if (args.length>0 && args[0].endsWith(".js"))
{
Setup.initHXCPPConfig(defines);
Setup.setupEmscripten(defines);
var node = defines.get("EMSCRIPTEN_NODE_JS");
Log.v( node==null ? "EMSCRIPTEN_NODE_JS undefined, using 'node'" : 'Using $node from EMSCRIPTEN_NODE_JS');
if (node=="" || node==null)
node = "node";
Log.v( node + " " + args.join(" ") );
Tools.exit( Sys.command( node, args ) );
}
else if (args.length==1 && args[0]=="defines")
{
var dir = '$HXCPP/tools/hxcpp';
try
{
var defineMatch = ~/m*defines\.\w+\("(\w+)"/i;
var allDefines = new Map<String,Bool>();
for(file in FileSystem.readDirectory(dir))
if (file.endsWith(".hx"))
for(line in sys.io.File.getContent(file).split("\n"))
if (defineMatch.match(line))
allDefines.set(defineMatch.matched(1),true);
for(key in allDefines.keys())
Sys.println(key);
}
catch(e:Dynamic)
{
Log.error('Could not read $dir : $e');
}
return;
}
isRPi = isLinux && Setup.isRaspberryPi();
is64 = getArch()!="m32";
var dirtyList = new Array<String>();
var a = 0;
while(a < args.length)
{
var arg = args[a];
if (arg.substr(0,2)=="-D" || (~/^[a-zA-Z0-9_][a-zA-Z0-9_-]*=/).match(arg) )
{
var define = arg.substr(0,2)=="-D" ? arg.substr(2) : arg;
var equals = define.indexOf("=");
if (equals>0)
{
var value = define.substr(equals+1);
define = define.substr(0,equals);
if (define=="destination")
{
destination = value;
}
else
defines.set(define,value);
}
else
defines.set(define,"");
if (define=="verbose")
Log.verbose = true;
}
else if (arg=="-debug")
defines.set("debug","1");
else if (arg=="-no-options")
optionsTxt = "";
else if (arg=="-options")
{
a++;
optionsTxt = args[a];
if (optionsTxt==null)
optionsTxt = "";
}
else if (arg=="-dirty")
{
a++;
dirtyList.push(args[a]);
}
else if (arg=="-v" || arg=="-verbose")
Log.verbose = true;
else if (arg=="-nocolor")
Log.colorSupported = false;
else if (arg.substr(0,2)=="-I")
include_path.push(PathManager.standardize(arg.substr(2)));
else if (makefile.length==0)
makefile = arg;
else
targets.push(arg);
a++;
}
if (defines.exists("HXCPP_TIMES"))
Profile.enable();
if (defines.exists("HXCPP_NO_COLOUR") || defines.exists("HXCPP_NO_COLOR"))
Log.colorSupported = false;
Log.verbose = Log.verbose || defines.exists("HXCPP_VERBOSE");
Log.quiet = defines.exists("HXCPP_QUIET") && !Log.verbose;
Log.mute = defines.exists("HXCPP_SILENT") && !Log.quiet && !Log.verbose;
if ( optionsTxt!="" && makefile!="")
{
var path = PathManager.combine(haxe.io.Path.directory(makefile), optionsTxt);
if (FileSystem.exists(path))
try
{
var contents = sys.io.File.getContent(path);
if (contents.substr(0,1)!=" ") // Is it New-style?
for(def in contents.split("\r").join("").split("\n"))
{
var equals = def.indexOf("=");
if (equals>0)
{
var name = def.substr(0,equals);
var value = def.substr(equals+1);
if (name=="hxcpp")
{
// Ignore
}
else if (name=="destination")
destination = value;
else
defines.set(name,value);
}
}
}
catch(e:Dynamic)
{
Log.error('Could not parse options file $path ($e)');
}
}
Profile.setEntry("setup");
Setup.initHXCPPConfig(defines);
if (HXCPP=="" && env.exists("HXCPP"))
{
HXCPP = PathManager.standardize(env.get("HXCPP"));
defines.set("HXCPP",HXCPP);
}
if (HXCPP=="")
{
if (!defines.exists("HXCPP"))
{
Log.error("Please run hxcpp using haxelib");
//throw "HXCPP not set, and not run from haxelib";
}
HXCPP = PathManager.standardize(defines.get("HXCPP"));
defines.set("HXCPP",HXCPP);
}
//Log.info("", "HXCPP : " + HXCPP);
include_path.push(".");
if (env.exists("HOME"))
include_path.push(env.get("HOME"));
if (env.exists("USERPROFILE"))
include_path.push(env.get("USERPROFILE"));
include_path.push(HXCPP);
//trace(include_path);
//var msvc = false;
// Create alias...
if (defines.exists("ios"))
{
if (defines.exists("simulator"))
defines.set("iphonesim", "iphonesim");
else if (!defines.exists ("iphonesim"))
defines.set("iphoneos", "iphoneos");
defines.set("iphone", "iphone");
}
if (defines.exists("tvos"))
{
if (defines.exists("simulator"))
defines.set("appletvsim", "appletvsim");
else if (!defines.exists ("appletvsim"))
defines.set("appletvos", "appletvos");
defines.set("appletv", "appletv");
}
if (makefile=="" || Log.verbose)
{
printBanner();
}
if (makefile=="")
{
printUsage();
}
else
{
Log.v('${BOLD}${YELLOW}Using makefile: $makefile${NORMAL}');
Log.v('${BOLD}${YELLOW}Reading HXCPP config: ' + defines.get("HXCPP_CONFIG") + NORMAL);
if (defines.exists("toolchain"))
Log.v('${BOLD}{$YELLOW}Using target toolchain: ' + defines.get("toolchain") + NORMAL);
else
Log.v('${BOLD}${YELLOW}No specified toolchain${NORMAL}');
if (Log.verbose) Log.println("");
if (targets.length==0)
targets.push("default");
new BuildTool(makefile,defines,targets,include_path,dirtyList);
}
}
static function printUsage()
{
Log.println('${YELLOW}Usage:${NORMAL}');
Log.println(' ${BOLD}haxelib run hxcpp${NORMAL} file.xml ${ITALIC}${WHITE}[options]${NORMAL}');
Log.println(' Build project from "file.xml". options:');
Log.println(' ${BOLD}-D${NORMAL}${ITALIC}value${NORMAL} -- Specify a define to use when processing other commands');
Log.println(' ${BOLD}-verbose${NORMAL} -- Print additional information (when available)');
Log.println(' ${BOLD}-dirty [groudId|all]${NORMAL} -- always rebuild files in given group');
Log.println(' ${BOLD}haxelib run hxcpp${NORMAL} ${ITALIC}${WHITE}file.cppia${NORMAL}');
Log.println(' Run cppia script using default Cppia host');
Log.println(' ${BOLD}haxelib run hxcpp${NORMAL} ${ITALIC}${WHITE}file.js${NORMAL}');
Log.println(' Run emscripten compiled scipt "file.js"');
Log.println(' ${BOLD}haxelib run hxcpp${NORMAL} ${ITALIC}${WHITE}cache [command] [project]${NORMAL}');
Log.println(' Perform command on cache, either on specific project or all. commands:');
Log.println(' ${BOLD}clear${NORMAL} -- remove all files from cache');
Log.println(' ${BOLD}days${NORMAL} #days -- remove files older than "days"');
Log.println(' ${BOLD}resize${NORMAL} #megabytes -- Only keep #megabytes MB');
Log.println(' ${BOLD}list${NORMAL} -- list cache usage');
Log.println(' ${BOLD}details${NORMAL} -- list cache usage, per file');
Log.println('');
}
static function printBanner()
{
Log.println("\x1b[33;1m __ ");
Log.println("/\\ \\ ");
Log.println("\\ \\ \\___ __ _ ___ _____ _____ ");
Log.println(" \\ \\ _ `\\ /\\ \\/'\\ /'___\\/\\ '__`\\/\\ '__`\\ ");
Log.println(" \\ \\ \\ \\ \\\\/> <//\\ \\__/\\ \\ \\L\\ \\ \\ \\L\\ \\");
Log.println(" \\ \\_\\ \\_\\/\\_/\\_\\ \\____\\\\ \\ ,__/\\ \\ ,__/");
Log.println(" \\/_/\\/_/\\//\\/_/\\/____/ \\ \\ \\/ \\ \\ \\/ ");
Log.println(" \\ \\_\\ \\ \\_\\ ");
Log.println(" \\/_/ \\/_/ \x1b[0m");
Log.println("");
Log.println("\x1b[1mhxcpp \x1b[0m\x1b[3;37m(Haxe C++ Runtime Support)\x1b[0m \x1b[1m(" + getVersion() + ")\x1b[0m");
Log.println("");
}
function setDefaultToolchain(defines:Hash<String>)
{
if ( (new EReg("window","i")).match(os) )
defines.set("windows_host","1");
if (defines.exists("iphoneos"))
{
defines.set("toolchain","iphoneos");
defines.set("iphone","iphone");
defines.set("apple","apple");
defines.set("BINDIR","iPhone");
}
else if (defines.exists("iphonesim"))
{
defines.set("toolchain","iphonesim");
defines.set("iphone","iphone");
defines.set("apple","apple");
defines.set("BINDIR","iPhone");
}
else if (defines.exists("appletvos"))
{
defines.set("toolchain","appletvos");
defines.set("appletv","appletv");
defines.set("apple","apple");
defines.set("BINDIR","AppleTV");
}
else if (defines.exists("appletvsim"))
{
defines.set("toolchain","appletvsim");
defines.set("appletv","appletv");
defines.set("apple","apple");
defines.set("BINDIR","AppleTV");
}
else if (defines.exists("watchos"))
{
defines.set("toolchain","watchos");
defines.set("apple","apple");
defines.set("applewatch","applewatch");
defines.set("BINDIR","watchos");
}
else if (defines.exists("watchsimulator"))
{
defines.set("toolchain","watchsimulator");
defines.set("applewatch","applewatch");
defines.set("apple","apple");
defines.set("BINDIR","watchsimulator");
}
else if (defines.exists("android"))
{
defines.set("toolchain","android");
defines.set("android","android");
defines.set("BINDIR","Android");
if (!defines.exists("ANDROID_HOST"))
{
if ( (new EReg("mac","i")).match(os) )
defines.set("ANDROID_HOST","darwin-x86_64");
else if ( (new EReg("window","i")).match(os) )
{
defines.set("windows_host","1");
defines.set("ANDROID_HOST","windows");
}
else if ( (new EReg("linux","i")).match(os) )
defines.set("ANDROID_HOST","linux-x86_64");
else
{
Log.error ("Unknown android host \"" + os + "\"");
//throw "Unknown android host:" + os;
}
}
}
else if (defines.exists("webos"))
{
defines.set("toolchain","webos");
defines.set("webos","webos");
defines.set("BINDIR","webOS");
}
else if (defines.exists("tizen"))
{
defines.set("toolchain","tizen");
defines.set("tizen","tizen");
defines.set("BINDIR","Tizen");
}
else if (defines.exists("blackberry"))
{
defines.set("toolchain", "blackberry");
defines.set("blackberry","blackberry");
defines.set("BINDIR","BlackBerry");
}
else if (defines.exists("emcc") || defines.exists("emscripten"))
{
defines.set("toolchain","emscripten");
defines.set("emcc","emcc");
defines.set("emscripten","emscripten");
defines.set("BINDIR","Emscripten");
}
else if (defines.exists("gph"))
{
defines.set("toolchain","gph");
defines.set("gph","gph");
defines.set("BINDIR","GPH");
}
else if (defines.exists ("gcw0"))
{
defines.set ("toolchain", "gcw0");
defines.set ("gcw0", "gcw0");
defines.set ("BINDIR", "GCW0");
}
else if (defines.exists("cygwin") || defines.exists("HXCPP_CYGWIN"))
{
set64(defines,m64);
defines.set("toolchain","cygwin");
defines.set("cygwin","cygwin");
defines.set("linux","linux");
defines.set("BINDIR",m64 ? "Cygwin64":"Cygwin");
}
else if ( (new EReg("window","i")).match(os) )
{
defines.set("windows_host","1");
// Cross-compile?
if (defines.exists("rpi"))
{
defines.set("toolchain","linux");
defines.set("xcompile","1");
defines.set("linux","linux");
defines.set("rpi","1");
defines.set("hardfp","1");
defines.set("BINDIR", "RPi");
}
else
{
set64(defines,m64,arm64);
defines.set("windows","windows");
defines.set("BINDIR",arm64 ? "WindowsArm64" : m64 ? "Windows64":"Windows");
// Choose between MSVC and MINGW
var useMsvc = true;
if (defines.exists("mingw") || defines.exists("HXCPP_MINGW") || defines.exists("minimingw"))
useMsvc = false;
else if ( defines.exists("winrt") || defines.exists("HXCPP_MSVC_VER"))
useMsvc = true;
else
{
for(i in 8...24)
{
if (Sys.getEnv("VS" + i + "0COMNTOOLS")!=null)
{
useMsvc = true;
break;
}
}
Log.v("Using Windows compiler: " + (useMsvc ? "MSVC" : "MinGW") );
}
if (useMsvc)
{
defines.set("toolchain","msvc");
if ( defines.exists("winrt") )
defines.set("BINDIR",m64 ? "WinRT64":"WinRT");
}
else
{
defines.set("toolchain","mingw");
defines.set("mingw","mingw");
}
}
}
else if ( isRPi )
{
defines.set("toolchain","linux");
defines.set("linux","linux");
defines.set("rpi","1");
defines.set("hardfp","1");
defines.set("BINDIR", "RPi");
}
else if ( (new EReg("linux","i")).match(os) )
{
set64(defines,m64,arm64);
// Cross-compile?
if(defines.exists("windows"))
{
defines.set("toolchain","mingw");
defines.set("xcompile","1");
defines.set("BINDIR", arm64 ? "WindowsArm64" : m64 ? "Windows64":"Windows");
}
else
{
defines.set("toolchain","linux");
defines.set("linux","linux");
if (defines.exists("HXCPP_LINUX_ARMV7"))
{
defines.set("noM32","1");
defines.set("noM64","1");
defines.set("HXCPP_ARMV7","1");
m64 = false;
}
else if (arm64 || defines.exists("HXCPP_LINUX_ARM64"))
{
defines.set("noM32","1");
defines.set("noM64","1");
defines.set("HXCPP_ARM64","1");
m64 = true;
}
defines.set("BINDIR", m64 ? "Linux64":"Linux");
}
}
else if ( (new EReg("mac","i")).match(os) )
{
set64(defines,m64, arm64);
// Cross-compile?
if (defines.exists("linux"))
{
defines.set("mac_host","1");
defines.set("linux","linux");
defines.set("toolchain","linux");
defines.set("xcompile","1");
defines.set("BINDIR", m64 ? "Linux64":"Linux");
}
else
{
defines.set("toolchain","mac");
defines.set("macos","macos");
defines.set("apple","apple");
defines.set("BINDIR", arm64 ? "MacArm64" : m64 ? "Mac64":"Mac");
}
}
}
function setupAppleDirectories(defines:Hash<String>)
{
if (defines.exists("HXCPP_CLEAN_ONLY"))
return;
if (defines.exists("apple") && !defines.exists("DEVELOPER_DIR"))
{
var developer_dir = ProcessManager.runProcessLine("", "xcode-select", ["--print-path"], true, false);
if (developer_dir == null || developer_dir == "" || developer_dir.indexOf ("Run xcode-select") > -1)
developer_dir = "/Applications/Xcode.app/Contents/Developer";
if (developer_dir == "/Developer")
defines.set("LEGACY_XCODE_LOCATION","1");
defines.set("DEVELOPER_DIR",developer_dir);
}
if (defines.exists("iphone") && !defines.exists("IPHONE_VER"))
{
var dev_path = defines.get("DEVELOPER_DIR") + "/Platforms/iPhoneOS.platform/Developer/SDKs/";
if (FileSystem.exists(dev_path))
{
var best="0.0";
var files = FileSystem.readDirectory(dev_path);
var extract_version = ~/^iPhoneOS(.*).sdk$/;
for(file in files)
{
if (extract_version.match(file))
{
var ver = extract_version.matched(1);
if (Std.parseFloat (ver)>Std.parseFloat (best))
best = ver;
}
}
if (best!="0.0")
defines.set("IPHONE_VER",best);
}
}
if (defines.exists("appletv") && !defines.exists("TVOS_VER"))
{
var dev_path = defines.get("DEVELOPER_DIR") + "/Platforms/AppleTVOS.platform/Developer/SDKs/";
if (FileSystem.exists(dev_path))
{
var best="0.0";
var files = FileSystem.readDirectory(dev_path);
var extract_version = ~/^AppleTVOS(.*).sdk$/;
for(file in files)
{
if (extract_version.match(file))
{
var ver = extract_version.matched(1);
if (Std.parseFloat (ver)>Std.parseFloat (best))
best = ver;
}
}
if (best!="0.0")
defines.set("TVOS_VER",best);
}
}
if (defines.exists("applewatch") && !defines.exists("WATCHOS_VER"))
{
var dev_path = defines.get("DEVELOPER_DIR") + "/Platforms/WatchOS.platform/Developer/SDKs/";
if (FileSystem.exists(dev_path))
{
var best="0.0";
var files = FileSystem.readDirectory(dev_path);
var extract_version = ~/^WatchOS(.*).sdk$/;
for(file in files)
{
if (extract_version.match(file))
{
var ver = extract_version.matched(1);
if (Std.parseFloat (ver)>Std.parseFloat (best))
best = ver;
}
}
if (best!="0.0")
defines.set("WATCHOS_VER",best);
}
}
if (defines.exists("macos") && !defines.exists("MACOSX_VER"))
{
var dev_path = defines.get("DEVELOPER_DIR") + "/Platforms/MacOSX.platform/Developer/SDKs/";
if (FileSystem.exists(dev_path))
{
var best="0.0";
var files = FileSystem.readDirectory(dev_path);
var extract_version = ~/^MacOSX(.*).sdk$/;
for(file in files)
{
if (extract_version.match(file))
{
var ver = extract_version.matched(1);
var split_best = best.split(".");
var split_ver = ver.split(".");
if (Std.parseFloat(split_ver[0]) > Std.parseFloat(split_best[0]) || Std.parseFloat(split_ver[1]) > Std.parseFloat(split_best[1]))
best = ver;
}
}
if (best!="0.0")
defines.set("MACOSX_VER",best);
else
Log.v("Could not find MACOSX_VER!");
}
}
if (!FileSystem.exists(defines.get("DEVELOPER_DIR") + "/Platforms/MacOSX.platform/Developer/SDKs/"))
{
defines.set("LEGACY_MACOSX_SDK","1");
}
}
function parseXML(inXML:XmlAccess,inSection:String, forceRelative:Bool)
{
for(el in inXML.elements)
{
if (valid(el,inSection))
{
switch(el.name)
{
case "set" :
var name = substitute(el.att.name);
var value = substitute(el.att.value);
mDefines.set(name,value);
case "unset" :
var name = substitute(el.att.name);
mDefines.remove(name);
case "setup" :
var name = substitute(el.att.name);
Setup.setup(name,mDefines);
case "echo" :
Log.info(substitute(el.att.value));
case "setenv" :
var name = substitute(el.att.name);
var value = substitute(el.att.value);
mDefines.set(name,value);
Sys.putEnv(name,value);
case "error" :
var error = substitute(el.att.value);
Log.error(error);
case "path" :
var path = substitute(el.att.name);
Log.info("", " - \x1b[1mAdding path:\x1b[0m " + path);
var sep = mDefines.exists("windows_host") ? ";" : ":";
var add = path + sep + Sys.getEnv("PATH");
Sys.putEnv("PATH", add);
//trace(Sys.getEnv("PATH"));
case "compiler" :
mCompiler = createCompiler(el,mCompiler);
case "stripper" :
mStripper = createStripper(el,mStripper);
case "manifest" :
mManifester = createManifester(el,mManifester);
case "prelinker" :
var name = substitute(el.att.id);
if (mPrelinkers.exists(name))
createPrelinker(el,mPrelinkers.get(name));
else
mPrelinkers.set(name, createPrelinker(el,null) );
case "linker" :
var name = substitute(el.att.id);
if (mLinkers.exists(name))
createLinker(el,mLinkers.get(name));
else
mLinkers.set(name, createLinker(el,null) );
case "files" :
var name = substitute(el.att.id);
var tags = el.has.tags ? substitute(el.att.tags) : null;
if (mFileGroups.exists(name) )
createFileGroup(el, mFileGroups.get(name), name, false, tags);
else
mFileGroups.set(name,createFileGroup(el,null,name, forceRelative,tags));
case "include", "import" :
var name = substitute(el.att.name);
var section = el.has.section ? substitute(el.att.section) : "";
include(name, section, el.has.noerror, el.name=="import" );
case "target" :
var name = substitute(el.att.id);
var overwrite = name=="default";
if (el.has.overwrite)
overwrite = true;
if (el.has.append)
overwrite = false;
if (mTargets.exists(name) && !overwrite)
createTarget(el,mTargets.get(name), forceRelative);
else
mTargets.set( name, createTarget(el,null, forceRelative) );
case "mkdir" :
var name = substitute(el.att.name);
try {
PathManager.mkdir(name);
} catch(e:Dynamic)
{
Log.error("Could not create directory " + name );
}
case "copy" :
var from = substitute(el.att.from);
from = PathManager.combine( Path.directory(mCurrentIncludeFile), from );
var to = substitute(el.att.to);
to = PathManager.combine( Path.directory(mCurrentIncludeFile), to );
copy(from,to);
case "copyFile" :
mCopyFiles.push(
new CopyFile(substitute(el.att.name),
substitute(el.att.from),
el.has.allowMissing ? subBool(el.att.allowMissing) : false,
el.has.overwrite ? substitute(el.att.overwrite) : Overwrite.ALWAYS,
el.has.toolId ? substitute(el.att.toolId) : null ) );
case "section" :
parseXML(el,"",forceRelative);
case "pleaseUpdateHxcppTool" :
checkToolVersion( substitute(el.att.version) );
case "magiclib" :
mMagicLibs.push( {name: substitute(el.att.name),
replace:substitute(el.att.replace) } );
case "nvccflag" :
if (el.has.name)
mNvccFlags.push( substitute(el.att.name) );
mNvccFlags.push( substitute(el.att.value) );
case "nvcclinkflag" :
if (el.has.name)
mNvccLinkFlags.push( substitute(el.att.name) );
mNvccLinkFlags.push( substitute(el.att.value) );
case "pragma" :
if (el.has.once)
mPragmaOnce.set(mCurrentIncludeFile, parseBool(substitute(el.att.once)));
}
}
}
}
public function checkToolVersion(inVersion:String)
{
var ver = Std.parseInt(inVersion);
if (ver>3)
Log.error("Your version of hxcpp.n is out-of-date. Please update.");
}
public function resolvePath(inPath:String)
{
return PathManager.combine( mCurrentIncludeFile=="" ? Sys.getCwd() : Path.directory(mCurrentIncludeFile),
inPath);
}
public function include(inName:String, inSection:String="", inAllowMissing:Bool = false, forceRelative=false)
{
var full_name = findIncludeFile(inName);
if (full_name!="")
{
if (mPragmaOnce.get(full_name))
return;
pushFile(full_name, "include", inSection);
// TODO - use mFileStack?
var oldInclude = mCurrentIncludeFile;
mCurrentIncludeFile = full_name;
var make_contents = sys.io.File.getContent(full_name);
var xml_slow = Xml.parse(make_contents);
Profile.push( haxe.io.Path.withoutDirectory(inName) );
parseXML(new XmlAccess(xml_slow.firstElement()),inSection, forceRelative);
Profile.pop();
mCurrentIncludeFile = oldInclude;
popFile();
}
else if (!inAllowMissing)
{
Log.error("Could not find include file \"" + inName + "\"");
//throw "Could not find include file " + name;
}
}
static function set64(outDefines:Hash<String>, in64:Bool,isArm64=false)
{
if (isArm64)
{
outDefines.set("HXCPP_ARM64","1");
outDefines.set("HXCPP_M64","1");
outDefines.remove("HXCPP_32");
}
else if (in64)
{
outDefines.set("HXCPP_M64","1");
outDefines.remove("HXCPP_32");
outDefines.remove("HXCPP_ARM64");
}
else
{
outDefines.set("HXCPP_M32","1");
outDefines.remove("HXCPP_M64");
outDefines.remove("HXCPP_ARM64");
}
}
public function dospath(path:String) : String
{
if (mDefines.exists("windows_host"))
{
path = path.split("\\").join("/");
var filename = "";
var parts = path.split("/");
if (!FileSystem.isDirectory(path))
filename = parts.pop();
var oldDir = Sys.getCwd();
var output = "";
var err = "";
Sys.setCwd(parts.join("\\"));
try {
var bat = '$HXCPP/toolchain/dospath.bat'.split("/").join("\\");
var process = new Process("cmd",["/c",bat]);
output = process.stdout.readAll().toString();
output = output.split("\r")[0].split("\n")[0];
err = process.stderr.readAll().toString();
process.close();
} catch (e:Dynamic) { Log.error(e); }
Sys.setCwd(oldDir);
if (output=="")
Log.error("Could not find dos path for " + path + " " + err);
return output + "\\" + filename;
}
return path;
}
public function substitute(str:String,needDollar=true):String
{
var match = needDollar ? mVarMatch : mNoDollarMatch;
while( match.match(str) )
{
var sub = match.matched(1);
if (sub.startsWith("haxelib:"))
{
sub = PathManager.getHaxelib(sub.substr(8));
sub = PathManager.standardize(sub);
}
else if (sub.startsWith("removeQuotes:"))
{
sub = mDefines.get(sub.substr(13));
var len = sub.length;
if (len>1 && sub.substr(0,1)=="\"" && sub.substr(len-1)=="\"")
sub = sub.substr(1,len-2);
}
else if (sub.startsWith("dospath:") )
{
sub = dospath( mDefines.get(sub.substr(8)) );
}
else if (sub.startsWith("dir:") )
{
sub = dospath( mDefines.get(sub.substr(4)) );
if (!FileSystem.isDirectory(sub))
{
sub = haxe.io.Path.directory(sub);
}
}
else if (sub=="this_dir")
{
sub = Path.normalize(mCurrentIncludeFile=="" ? Sys.getCwd() : Path.directory(mCurrentIncludeFile));
}
else
sub = mDefines.get(sub);
if (sub==null) sub="";
str = match.matchedLeft() + sub + match.matchedRight();
}
return str;
}
public function subBool(str:String):Bool
{
var result = substitute(str);
return result=="t" || result=="true" || result=="1";
}
public function valid(inEl:XmlAccess,inSection:String):Bool
{
if (inEl.x.get("if") != null)
{
var value = inEl.x.get("if");
var optionalDefines = value.split("||");
var matchOptional = false;
for (optional in optionalDefines)
{
var requiredDefines = optional.split(" ");
var matchRequired = true;
for (required in requiredDefines)
{
var check = StringTools.trim(required);
if (check != "" && !defined(check))
{
matchRequired = false;
}
}
if (matchRequired)
{
matchOptional = true;
}
}
if (optionalDefines.length > 0 && !matchOptional)
{
return false;
}
}
if (inEl.has.unless)
{
var value = substitute(inEl.att.unless);
var optionalDefines = value.split("||");
var matchOptional = false;
for (optional in optionalDefines)
{
var requiredDefines = optional.split(" ");
var matchRequired = true;
for (required in requiredDefines)
{
var check = StringTools.trim(required);
if (check != "" && !defined(check))
{
matchRequired = false;
}
}
if (matchRequired)
{
matchOptional = true;
}
}
if (optionalDefines.length > 0 && matchOptional)
{
return false;
}
}
if (inEl.has.unlessApi)
{
var value = substitute(inEl.att.unlessApi);
try {
var val = Std.parseInt(value);
if (val<=SupportedVersion)
return false;
} catch(e:Dynamic) { }
}
if (inEl.has.ifExists)
if (!FileSystem.exists( substitute(inEl.att.ifExists) )) return false;
if (inSection!="")
{
if (inEl.name!="section")
return false;
if (!inEl.has.id)
return false;
if (inEl.att.id!=inSection)
return false;
}
return true;
}
}