import haxe.io.Path; import sys.FileSystem; import haxe.crypto.Md5; using StringTools; class Linker { public var mExe:String; public var mFlags:Array; public var mOutFlag:String; public var mExt:String; public var mNamePrefix:String; public var mLibDir:String; public var mRanLib:String; public var mFromFile:String; public var mFromFileNeedsQuotes:Bool; public var mLibs:Array; public var mExpandArchives:Bool; public var mRecreate:Bool; public var mLastOutName:String; public var mAddLibPath:String; public function new(inExe:String) { mFlags = []; mOutFlag = "-o"; mAddLibPath = "-L"; mExe = inExe; mNamePrefix = ""; mLibDir = ""; mRanLib = ""; mExpandArchives = false; // Default to on... mFromFile = "@"; mFromFileNeedsQuotes = true; mLibs = []; mRecreate = false; } function isOutOfDate(inName:String, inObjs:Array) { if (!FileSystem.exists(inName)) return true; var stamp = FileSystem.stat(inName).mtime.getTime(); for(obj in inObjs) { if (!FileSystem.exists(obj)) { Log.error("Could not find \"" + obj + "\" required by \"" + inName + "\""); //throw "Could not find " + obj + " required by " + inName; } var obj_stamp = FileSystem.stat(obj).mtime.getTime(); if (obj_stamp > stamp) return true; } return false; } function getSimpleFilename(inTarget:Target) { var ext = inTarget.getExt(mExt); // Remove arch from ext ... var idx = ext.indexOf('.'); if (idx>0) ext = ext.substr(idx); return mNamePrefix + inTarget.mOutput + ext; } public function getUnstrippedFilename(inObjDir, inTarget:Target) { if (inTarget.mFullUnstrippedName!=null) { PathManager.mkdir( inTarget.mFullUnstrippedName, true ); return inTarget.mFullUnstrippedName; } return inObjDir + "/" + getSimpleFilename(inTarget); } public function link(inTarget:Target,inObjs:Array,inCompiler:Compiler,extraDeps:Array ) { var ext = inTarget.getExt(mExt); var file_name = mNamePrefix + inTarget.mOutput + ext; var tmpDir = inCompiler.mObjDir; try { PathManager.mkdir(inTarget.mOutputDir); } catch (e:Dynamic) { Log.error("Unable to create output directory \"" + inTarget.mOutputDir + "\""); //throw "Unable to create output directory " + inTarget.mOutputDir; } var out_name = Path.normalize(PathManager.combine( inTarget.mBuildDir, inTarget.mOutputDir + file_name)); var hashFile = out_name + ".hash"; if (inTarget.mFullOutputName!=null) { PathManager.mkdir( inTarget.mFullOutputName, true ); out_name = inTarget.mFullOutputName; } mLastOutName = out_name; var lastLib = ""; var libs = new Array(); for(l in inTarget.mAutoLibs) if (l!=lastLib) { libs.push(l); lastLib = l; } for(l in inTarget.mLibs) if (l!=lastLib) { libs.push(l); lastLib = l; } for(l in mLibs) if (l!=lastLib) { libs.push(l); lastLib = l; } var v18Added = false; var isOutOfDateLibs = false; var md5 = Md5.encode(inObjs.join(";")); if (!FileSystem.exists(hashFile) || sys.io.File.getContent(hashFile)!=md5) isOutOfDateLibs = true; for(i in 0...libs.length) { var lib = libs[i]; var parts = lib.split("{MSVC_VER}"); if (parts.length==2) { var ver = ""; if (BuildTool.isMsvc()) { var current = parts[0] + "-" + BuildTool.getMsvcVer() + parts[1]; if (FileSystem.exists(current)) { Log.info("", " - \x1b[1mUsing current compiler library:\x1b[0m " + current); libs[i]=current; } else { var v18 = parts[0] + "-18" + parts[1]; if (FileSystem.exists(v18)) { Log.info("", " - \x1b[1mUsing MSVC18 compatible library:\x1b[0m " + v18); libs[i]=v18; if (!v18Added) { v18Added=true; libs.push( BuildTool.HXCPP + "/lib/Windows/libmsvccompat-18.lib"); } } else { Log.error("Could not find compatible library for " + lib + ", " + v18 + " does not exist"); } } } else libs[i] = parts[0] + parts[1]; } if (!isOutOfDateLibs) { var lib = libs[i]; if (FileSystem.exists(lib)) isOutOfDateLibs = isOutOfDate(out_name,[lib]); } if (BuildTool.isMingw()) { var libMatch = ~/^([a-zA-Z0-9_]+).lib$/; if (libMatch.match(libs[i])) libs[i] = "-l" + libMatch.matched(1); } } if (isOutOfDateLibs || isOutOfDate(out_name,inObjs) || isOutOfDate(out_name,inTarget.mDepends) || isOutOfDate(out_name,extraDeps) ) { var args = new Array(); var out = mOutFlag; if (out.substr(-1)==" ") { args.push(out.substr(0,out.length-1)); out = ""; } // Build in temp dir, and then move out so all the crap windows // creates stays out of the way if (mLibDir!="") { PathManager.mkdir(mLibDir); args.push(out + mLibDir + "/" + file_name); } else { if (mRecreate && FileSystem.exists(out_name)) { Log.info("\x1b[1mClean: \x1b[0m" + out_name); FileSystem.deleteFile(out_name); } args.push(out + out_name); } args = args.concat(mFlags).concat(inTarget.mFlags); var objs = inObjs.copy(); if (mExpandArchives) { var isArchive = ~/\.a$/; var libArgs = new Array(); for(lib in libs) { if (isArchive.match(lib)) { var libName = Path.withoutDirectory(lib); var libObjs = ProcessManager.readStdout(mExe, ["t", lib ]); var objDir = tmpDir + "/" + libName + ".unpack"; PathManager.mkdir(objDir); ProcessManager.runCommand (objDir, mExe, ["x", lib], true, true, false, " - Unpack : " + lib); for(obj in libObjs) objs.push( objDir+"/"+obj ); } else libArgs.push(lib); } libs = libArgs; } var here = Path.normalize(Sys.getCwd()) + "/"; var hereLen = here.length; for(oid in 0...objs.length) { var obj = Path.normalize( objs[oid] ); if (obj.startsWith(here)) objs[oid] = obj.substr(hereLen); } // Place list of obj files in a file called "all_objs" if (mFromFile!="") { PathManager.mkdir(tmpDir); var fname = tmpDir + "/all_objs"; var local = Path.normalize(fname); if (local.startsWith(here)) fname = local.substr(hereLen); var fout = sys.io.File.write(fname,false); if (mFromFileNeedsQuotes) { for(obj in objs) fout.writeString('"' + obj + '"\n'); } else { for(obj in objs) fout.writeString(obj + '\n'); } fout.close(); var parts = mFromFile.split(" "); var last = parts.pop(); args = args.concat(parts); args.push(last + fname ); } else args = args.concat(objs); for(libpath in inTarget.mLibPaths) { var path = Path.normalize(libpath); if (path.startsWith(here)) path = path.substr(hereLen); args.push( mAddLibPath + path ); } args = args.concat(libs); var result = ProcessManager.runCommand("", mExe, args, true, true, false, "\x1b[1mLink: \x1b[0m" + out_name); if (result!=0) { Tools.exit(result); //throw "Error : " + result + " - build cancelled"; } if (mRanLib!="") { args = [out_name]; var result = ProcessManager.runCommand("", mRanLib, args, true, true, false, "\x1b[1mRanlib:\x1b[0m " + out_name); if (result!=0) { Tools.exit(result); //throw "Error : " + result + " - build cancelled"; } } if (mLibDir!="") { sys.io.File.copy( mLibDir+"/"+file_name, out_name ); FileSystem.deleteFile( mLibDir+"/"+file_name ); } sys.io.File.saveContent(hashFile,md5); return out_name; } return ""; } }