643 lines
18 KiB
Haxe
643 lines
18 KiB
Haxe
import haxe.crypto.Md5;
|
|
import haxe.io.Path;
|
|
import sys.FileSystem;
|
|
|
|
private class FlagInfo
|
|
{
|
|
var flag:String;
|
|
var tag:String;
|
|
public function new(inFlag:String, inTag:String)
|
|
{
|
|
flag = inFlag;
|
|
tag = inTag;
|
|
}
|
|
public function add(args:Array<String>, inFilter:Array<String>)
|
|
{
|
|
var allowSpace = inFilter.indexOf("nvcc")<0;
|
|
if ((tag==""&&allowSpace) || inFilter.indexOf(tag)>=0)
|
|
args.push(flag);
|
|
}
|
|
public function toString():String
|
|
{
|
|
if (tag=="")
|
|
return flag;
|
|
else
|
|
return '$flag($tag)';
|
|
}
|
|
}
|
|
|
|
class Compiler
|
|
{
|
|
private var mFlags:Array<FlagInfo>;
|
|
public var mCFlags:Array<String>;
|
|
public var mNvccFlags:Array<String>;
|
|
public var mMMFlags:Array<String>;
|
|
public var mCPPFlags:Array<String>;
|
|
public var mOBJCFlags:Array<String>;
|
|
public var mPCHFlags:Array<String>;
|
|
public var mAddGCCIdentity:Bool;
|
|
public var mExe:String;
|
|
public var mOutFlag:String;
|
|
public var mObjDir:String;
|
|
public var mRelObjDir:String;
|
|
public var mExt:String;
|
|
|
|
public var mPCHExt:String;
|
|
public var mPCHCreate:String;
|
|
public var mPCHUse:String;
|
|
public var mPCHFilename:String;
|
|
public var mPCH:String;
|
|
|
|
public var mRcExe:String;
|
|
public var mRcExt:String;
|
|
public var mRcFlags:Array<String>;
|
|
|
|
public var mGetCompilerVersion:String;
|
|
public var mCompilerVersion:String;
|
|
public var mCompilerVersionString:String;
|
|
public var mCached:Bool;
|
|
|
|
public var mID:String;
|
|
|
|
//testing...
|
|
public var useCacheInPlace = true;
|
|
//public var useCacheInPlace = false;
|
|
|
|
public function new(inID,inExe:String)
|
|
{
|
|
mFlags = [];
|
|
mCFlags = [];
|
|
mNvccFlags = [];
|
|
mCPPFlags = [];
|
|
mOBJCFlags = [];
|
|
mMMFlags = [];
|
|
mPCHFlags = [];
|
|
mAddGCCIdentity = false;
|
|
mCompilerVersion = null;
|
|
mRcExt = ".res";
|
|
mObjDir = "obj";
|
|
mOutFlag = "-o";
|
|
mExe = inExe;
|
|
mID = inID;
|
|
mExt = ".o";
|
|
mPCHExt = ".pch";
|
|
mPCHCreate = "-Yc";
|
|
mPCHUse = "-Yu";
|
|
mPCHFilename = "/Fp";
|
|
mCached = false;
|
|
mRcFlags = [];
|
|
}
|
|
|
|
public function getFlagStrings()
|
|
{
|
|
var result = new Array<String>();
|
|
for(f in mFlags)
|
|
result.push( f.toString() );
|
|
return result;
|
|
}
|
|
|
|
public function addFlag(inFlag:String, inTag:String)
|
|
{
|
|
mFlags.push( new FlagInfo(inFlag, inTag) );
|
|
}
|
|
|
|
|
|
public function objToAbsolute()
|
|
{
|
|
if (mRelObjDir==null)
|
|
mRelObjDir = mObjDir;
|
|
mObjDir = Path.normalize( PathManager.combine( Sys.getCwd(), mRelObjDir ) );
|
|
}
|
|
|
|
public function getTargetPrefix()
|
|
{
|
|
var dir = mRelObjDir!=null ? mRelObjDir : mObjDir;
|
|
dir = dir.split("\\").join("/");
|
|
var parts = dir.split("/");
|
|
// Trailing slash?
|
|
var prefix = parts.pop();
|
|
if (prefix=="")
|
|
prefix = parts.pop();
|
|
if (prefix==null)
|
|
prefix = "";
|
|
prefix = prefix.split("-").join("_");
|
|
return prefix;
|
|
}
|
|
|
|
function addIdentity(ext:String,ioArgs:Array<String>)
|
|
{
|
|
if (mAddGCCIdentity)
|
|
{
|
|
var identity = switch(ext)
|
|
{
|
|
case "c" : "c";
|
|
case "m" : "objective-c";
|
|
case "mm" : "objective-c++";
|
|
case "cpp" : "c++";
|
|
case "c++" : "c++";
|
|
default:"";
|
|
}
|
|
if (identity!="")
|
|
{
|
|
ioArgs.push("-x");
|
|
ioArgs.push(identity);
|
|
}
|
|
}
|
|
}
|
|
|
|
function addOptimTags(tagFilter:Array<String>)
|
|
{
|
|
var optimFlags = (tagFilter.indexOf("debug") >= 0 ? 1 : 0) +
|
|
(tagFilter.indexOf("release") >= 0 ? 1 : 0) +
|
|
(tagFilter.indexOf("optim-std") >= 0 ? 1 : 0) +
|
|
(tagFilter.indexOf("optim-none") >= 0 ? 1 : 0) +
|
|
(tagFilter.indexOf("optim-size") >= 0 ? 1 : 0);
|
|
if (optimFlags==0)
|
|
tagFilter.push("optim-std");
|
|
else if (optimFlags>1)
|
|
Log.error("More than one optimization tag has been set:" + tagFilter);
|
|
}
|
|
|
|
|
|
public function getCompilerDefines(inTags:String)
|
|
{
|
|
var args = new Array<String>();
|
|
var tagFilter = inTags.split(",");
|
|
addOptimTags(tagFilter);
|
|
for(flag in mFlags)
|
|
flag.add(args,tagFilter);
|
|
return args;
|
|
}
|
|
|
|
function getArgs(inFile:File)
|
|
{
|
|
var nvcc = inFile.isNvcc();
|
|
var isRc = mRcExe!=null && inFile.isResource();
|
|
var args = nvcc ? inFile.mGroup.mCompilerFlags.concat( BuildTool.getNvccFlags() ) :
|
|
inFile.mCompilerFlags.concat(inFile.mGroup.mCompilerFlags);
|
|
var tagFilter = inFile.getTags().split(",");
|
|
addOptimTags(tagFilter);
|
|
if (!isRc)
|
|
for(flag in mFlags)
|
|
flag.add(args,tagFilter);
|
|
var ext = mExt.toLowerCase();
|
|
var ext = new Path(inFile.mName).ext;
|
|
if (ext!=null)
|
|
ext = ext.toLowerCase();
|
|
else
|
|
Log.error("Unkown extension for " + inFile.mName);
|
|
|
|
|
|
addIdentity(ext,args);
|
|
|
|
var allowPch = false;
|
|
if (nvcc)
|
|
args = args.concat(mNvccFlags);
|
|
else if (isRc)
|
|
args = args.concat(mRcFlags);
|
|
else if (ext=="c")
|
|
args = args.concat(mCFlags);
|
|
else if (ext=="m")
|
|
args = args.concat(mOBJCFlags);
|
|
else if (ext=="mm")
|
|
args = args.concat(mMMFlags);
|
|
else if (ext=="cpp" || ext=="c++" || ext=="cc")
|
|
{
|
|
allowPch = true;
|
|
args = args.concat(mCPPFlags);
|
|
}
|
|
|
|
if (isRc || inFile.getTags()!=inFile.mGroup.getTags())
|
|
allowPch = false;
|
|
|
|
if (inFile.mGroup.isPrecompiled() && allowPch)
|
|
{
|
|
var pchDir = getPchDir(inFile.mGroup);
|
|
if (mPCHUse!="")
|
|
{
|
|
args.push(mPCHUse + inFile.mGroup.mPrecompiledHeader + ".h");
|
|
args.push(mPCHFilename + pchDir + "/" + inFile.mGroup.getPchName() + mPCHExt);
|
|
}
|
|
else
|
|
args.unshift("-I"+pchDir);
|
|
}
|
|
|
|
return args;
|
|
}
|
|
|
|
public function createEmbedFile(srcName:String, destName:String, embedName:String, scramble:String)
|
|
{
|
|
try
|
|
{
|
|
var content = sys.io.File.getContent(srcName);
|
|
var output = new Array<String>();
|
|
|
|
if (scramble==null)
|
|
{
|
|
output.push( 'const char *$embedName = ' );
|
|
content = content.split("\r").join("");
|
|
content = content.split("\\").join( String.fromCharCode(1) );
|
|
content = content.split('"').join('\\"');
|
|
content = content.split(String.fromCharCode(1)).join("\\\\" );
|
|
var lines = content.split("\n");
|
|
for(line in lines)
|
|
output.push( '"$line\\n"' );
|
|
output.push(";\n");
|
|
}
|
|
else
|
|
{
|
|
var bytes = haxe.io.Bytes.ofString(content);
|
|
var byteLen = bytes.length;
|
|
var key = haxe.io.Bytes.ofString(scramble);
|
|
var keyLen = key.length;
|
|
var state = 0;
|
|
var line = "";
|
|
|
|
output.push( 'int ${embedName}_len = $byteLen;' );
|
|
output.push( 'static const unsigned char data[] = {' );
|
|
for(i in 0...byteLen)
|
|
{
|
|
var ch = bytes.get(i);
|
|
state = (((state + key.get(i%keyLen)) ^ ch) & 0xff);
|
|
line += state + ",";
|
|
if ( (i%10)==9 )
|
|
{
|
|
output.push(line);
|
|
line = "";
|
|
}
|
|
}
|
|
if (line!="")
|
|
output.push(line);
|
|
output.push( '};' );
|
|
output.push( 'const unsigned char * $embedName = data;' );
|
|
}
|
|
sys.io.File.saveContent(destName, output.join("\n") );
|
|
}
|
|
catch(e:Dynamic)
|
|
{
|
|
Log.warn('Error creating $destName from $srcName: $e');
|
|
}
|
|
}
|
|
|
|
public function cleanTmp(file:String)
|
|
{
|
|
if (BuildTool.keepTemp())
|
|
return;
|
|
|
|
try
|
|
{
|
|
if (file!=null && FileSystem.exists(file))
|
|
FileSystem.deleteFile(file);
|
|
}
|
|
catch(e:Dynamic) { }
|
|
}
|
|
|
|
public function compile(inFile:File,inTid:Int,headerFunc:Void->Void,pchTimeStamp:Null<Float>)
|
|
{
|
|
var obj_name = getObjName(inFile);
|
|
var args = getArgs(inFile);
|
|
var nvcc = inFile.isNvcc();
|
|
var exe = nvcc ? BuildTool.getNvcc() : mExe;
|
|
var isRc = mRcExe!=null && inFile.isResource();
|
|
if (isRc)
|
|
exe = mRcExe;
|
|
|
|
var found = false;
|
|
var cacheName:String = null;
|
|
if (mCompilerVersion!=null && inFile.mGroup.isCached())
|
|
{
|
|
cacheName = getHashedName(inFile, args);
|
|
if (useCacheInPlace)
|
|
{
|
|
//Log.info(""," link cache for " + obj_name );
|
|
obj_name = cacheName;
|
|
}
|
|
|
|
if (FileSystem.exists(cacheName))
|
|
{
|
|
var newer = true;
|
|
if (pchTimeStamp!=null || inFile.mGroup.mRespectTimestamp)
|
|
{
|
|
var time = FileSystem.stat(cacheName).mtime.getTime();
|
|
|
|
if (pchTimeStamp!=null)
|
|
newer = time>=pchTimeStamp;
|
|
if (inFile.mGroup.mRespectTimestamp)
|
|
newer = newer && time>= FileSystem.stat(inFile.mDir + "/" + inFile.mName).mtime.getTime();
|
|
}
|
|
if (newer)
|
|
{
|
|
//Log.info(""," copy cache for " + obj_name );
|
|
if (!useCacheInPlace)
|
|
sys.io.File.copy(cacheName, obj_name);
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
if (headerFunc!=null)
|
|
headerFunc();
|
|
|
|
var tmpFile:String = null;
|
|
var delayedFilename:String = null;
|
|
|
|
if (inFile.mEmbedName!=null)
|
|
{
|
|
var srcDir = Path.directory( inFile.mDir + "/" + inFile.mName);
|
|
tmpFile = new Path( srcDir + "/" + inFile.mEmbedName + ".cpp").toString();
|
|
Log.v("Creating temp file " + tmpFile);
|
|
createEmbedFile( inFile.mDir + "/" + inFile.mName, tmpFile, inFile.mEmbedName, inFile.mScramble );
|
|
args.push( tmpFile );
|
|
}
|
|
else
|
|
{
|
|
if (isRc)
|
|
delayedFilename = (new Path( inFile.mDir + inFile.mName)).toString();
|
|
else
|
|
args.push( (new Path( inFile.mDir + inFile.mName)).toString() );
|
|
}
|
|
|
|
var out = nvcc ? "-o " : mOutFlag;
|
|
if (out.substr(-1)==" ")
|
|
{
|
|
args.push(out.substr(0,out.length-1));
|
|
out = "";
|
|
}
|
|
|
|
args.push(out + obj_name);
|
|
|
|
if (delayedFilename!=null)
|
|
args.push(delayedFilename);
|
|
|
|
var tagInfo = inFile.mTags==null ? "" : " " + inFile.mTags.split(",");
|
|
|
|
var fileName = inFile.mName;
|
|
var split = fileName.split ("/");
|
|
if (split.length > 1)
|
|
{
|
|
fileName = " \x1b[2m-\x1b[0m \x1b[33m" + split.slice(0, split.length - 1).join("/") + "/\x1b[33;1m" + split[split.length - 1] + "\x1b[0m";
|
|
}
|
|
else
|
|
{
|
|
fileName = " \x1b[2m-\x1b[0m \x1b[33;1m" + fileName + "\x1b[0m";
|
|
}
|
|
fileName += " \x1b[3m" + tagInfo + "\x1b[0m";
|
|
|
|
|
|
if (inTid >= 0)
|
|
{
|
|
if (BuildTool.threadExitCode == 0)
|
|
{
|
|
if (!Log.verbose)
|
|
{
|
|
Log.info(fileName);
|
|
}
|
|
var err = ProcessManager.runProcessThreaded(exe, args, null);
|
|
cleanTmp(tmpFile);
|
|
if (err!=0)
|
|
{
|
|
if (FileSystem.exists(obj_name))
|
|
FileSystem.deleteFile(obj_name);
|
|
BuildTool.setThreadError(err);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!Log.verbose)
|
|
{
|
|
Log.info(fileName);
|
|
}
|
|
var result = ProcessManager.runProcessThreaded(exe, args, null);
|
|
cleanTmp(tmpFile);
|
|
if (result!=0)
|
|
{
|
|
if (FileSystem.exists(obj_name))
|
|
FileSystem.deleteFile(obj_name);
|
|
Tools.exit (result);
|
|
//throw "Error : " + result + " - build cancelled";
|
|
}
|
|
}
|
|
|
|
if (cacheName!=null && !useCacheInPlace)
|
|
{
|
|
Log.info("", " caching " + cacheName);
|
|
sys.io.File.copy(obj_name, cacheName);
|
|
}
|
|
}
|
|
|
|
return obj_name;
|
|
}
|
|
|
|
public function createCompilerVersion(inGroup:FileGroup)
|
|
{
|
|
if ( mCompilerVersion==null)
|
|
{
|
|
var versionString = "";
|
|
var command = "";
|
|
|
|
if (mGetCompilerVersion==null)
|
|
{
|
|
command = mExe + " --version";
|
|
versionString = ProcessManager.readStdout(mExe,["--version"]).join(" ");
|
|
}
|
|
else
|
|
{
|
|
command = mGetCompilerVersion;
|
|
versionString = ProcessManager.readStderr(mGetCompilerVersion,[]).join(" ");
|
|
}
|
|
|
|
if (versionString=="" || versionString==null)
|
|
Log.error("Could not deduce compiler version with " + command);
|
|
|
|
Log.info("", "Compiler version: " + versionString);
|
|
|
|
mCompilerVersionString = versionString;
|
|
mCompilerVersion = Md5.encode(versionString);
|
|
mCached = true;
|
|
}
|
|
|
|
return mCached;
|
|
}
|
|
|
|
function getObjName(inFile:File)
|
|
{
|
|
var isRc = mRcExe!=null && inFile.isResource();
|
|
|
|
var path = new Path(inFile.mName);
|
|
var dirId = Md5.encode(BuildTool.targetKey + path.dir + inFile.mGroup.mId).substr(0,8) + "_";
|
|
|
|
return PathManager.combine(mObjDir, inFile.mGroup.mObjPrefix + dirId + path.file + (isRc ? mRcExt : mExt) );
|
|
}
|
|
|
|
function getHashedName(inFile:File, args:Array<String>)
|
|
{
|
|
var sourceName = inFile.mDir + inFile.mName;
|
|
var contents = sys.io.File.getContent(sourceName);
|
|
if (contents!="")
|
|
{
|
|
var md5 = Md5.encode(contents + args.join(" ") +
|
|
inFile.mGroup.mDependHash + mCompilerVersion + inFile.mDependHash );
|
|
return CompileCache.getCacheName(inFile.getCacheProject(),md5,mExt);
|
|
}
|
|
else
|
|
throw "Unkown source contents " + sourceName;
|
|
return "";
|
|
}
|
|
|
|
public function getCacheString(inFile:File)
|
|
{
|
|
var args = getArgs(inFile);
|
|
return ("<contents>" + args.join(" ") + " " + inFile.mGroup.getDependString() + " " + mCompilerVersionString + " " + inFile.getDependString() );
|
|
}
|
|
|
|
public function getCachedObjName(inFile:File)
|
|
{
|
|
if (mCompilerVersion!=null && useCacheInPlace && inFile.mGroup.isCached())
|
|
{
|
|
//trace(inFile.mName + " " + inFile.getTags().split(",") + " " + getFlagStrings() + " " + getArgs(inFile));
|
|
return getHashedName(inFile, getArgs(inFile));
|
|
}
|
|
else
|
|
return getObjName(inFile);
|
|
}
|
|
|
|
public function needsPchObj()
|
|
{
|
|
return mPCH!="gcc";
|
|
}
|
|
|
|
/*
|
|
public function getPchObjName(group:FileGroup)
|
|
{
|
|
var pchDir = getPchDir(group);
|
|
if (pchDir != "")
|
|
return pchDir + "/" + group.getPchName() + mExt;
|
|
throw "Missing precompiled directory name";
|
|
}
|
|
*/
|
|
public function getPchCompileFlags(inGroup:FileGroup)
|
|
{
|
|
var args = inGroup.mCompilerFlags.copy();
|
|
var tags = inGroup.mTags.split(",");
|
|
addOptimTags(tags);
|
|
for(flag in mFlags)
|
|
flag.add(args,tags);
|
|
|
|
return args.concat( mCPPFlags );
|
|
}
|
|
|
|
public function getPchDir(inGroup:FileGroup)
|
|
{
|
|
if (!inGroup.isCached())
|
|
return inGroup.getPchDir(mObjDir);
|
|
|
|
var args = getPchCompileFlags(inGroup);
|
|
var md5 = Md5.encode(args.join(" ") + inGroup.mPrecompiledHeader +
|
|
inGroup.mDependHash + mCompilerVersion );
|
|
return CompileCache.getPchDir(inGroup.getCacheProject(),md5);
|
|
}
|
|
|
|
public function precompile(inGroup:FileGroup, inReuseIfPossible:Bool)
|
|
{
|
|
// header will be like "hxcpp" or "wx/wx"
|
|
var header = inGroup.mPrecompiledHeader;
|
|
// file will be like "hxcpp" or "wx"
|
|
var file = inGroup.getPchName();
|
|
|
|
var args = getPchCompileFlags(inGroup);
|
|
|
|
// Local output dir
|
|
var dir = getPchDir(inGroup);
|
|
|
|
// Like objs/hxcpp.pch or objs/wx.gch
|
|
var pch_name = dir + "/" + file + mPCHExt;
|
|
if (inGroup.isCached() || inReuseIfPossible)
|
|
{
|
|
// No obj needed for gcc
|
|
var obj = mPCH=="gcc" ? null : PathManager.combine(dir, file + mExt);
|
|
if (FileSystem.exists(pch_name) && (obj==null || FileSystem.exists(obj)) )
|
|
return obj;
|
|
}
|
|
|
|
args = args.concat( mPCHFlags );
|
|
|
|
|
|
//Log.info("", "Make pch dir " + dir );
|
|
PathManager.mkdir(dir);
|
|
|
|
if (mPCH!="gcc")
|
|
{
|
|
args.push( mPCHCreate + header + ".h" );
|
|
var symbol = "link" + Md5.encode( PathManager.combine(dir, file + mExt) );
|
|
args.push( "-Yl" + symbol );
|
|
|
|
// Create a temp file for including ...
|
|
var tmp_cpp = dir + "/" + file + ".cpp";
|
|
var outFile = sys.io.File.write(tmp_cpp,false);
|
|
outFile.writeString("#include <" + header + ".h>\n");
|
|
outFile.close();
|
|
|
|
args.push( tmp_cpp );
|
|
args.push(mPCHFilename + pch_name);
|
|
args.push(mOutFlag + PathManager.combine(dir, file + mExt));
|
|
}
|
|
else
|
|
{
|
|
Log.info("", 'Creating PCH directory "$dir"');
|
|
PathManager.mkdir(dir);
|
|
args.push( "-o" );
|
|
args.push(pch_name);
|
|
args.push( inGroup.mPrecompiledHeaderDir + "/" + inGroup.mPrecompiledHeader + ".h" );
|
|
}
|
|
|
|
Log.info("Creating " + pch_name + "...", " - Precompile " + pch_name );
|
|
var result = ProcessManager.runCommand("", mExe, args);
|
|
if (result!=0)
|
|
{
|
|
var goes = 10;
|
|
for(attempt in 0...goes)
|
|
{
|
|
try {
|
|
if (FileSystem.exists(pch_name))
|
|
FileSystem.deleteFile(pch_name);
|
|
break;
|
|
}
|
|
catch(error:Dynamic)
|
|
{
|
|
Log.warn('Error cleaning PCH file $pch_name: $error');
|
|
if (attempt<goes-1)
|
|
Sys.sleep(0.25);
|
|
}
|
|
}
|
|
Log.error("Could not create PCH");
|
|
//throw "Error creating pch: " + result + " - build cancelled";
|
|
}
|
|
|
|
if (mPCH!="gcc")
|
|
return PathManager.combine(dir, file + mExt);
|
|
return null;
|
|
}
|
|
|
|
public function setPCH(inPCH:String)
|
|
{
|
|
mPCH = inPCH;
|
|
if (mPCH=="gcc")
|
|
{
|
|
mPCHExt = ".h.gch";
|
|
mPCHUse = "";
|
|
mPCHFilename = "";
|
|
}
|
|
}
|
|
|
|
public function initPrecompile(inDefault:String)
|
|
{
|
|
if (mPCH==null)
|
|
setPCH(inDefault);
|
|
return mPCH!=null;
|
|
}
|
|
|
|
}
|