749 lines
24 KiB
Haxe
Raw Normal View History

2025-01-22 16:18:30 +01:00
import haxe.io.Eof;
import sys.io.Process;
import sys.FileSystem;
import BuildTool;
class Setup
{
static function findAndroidNdkRoot(defines: Map<String,String>, inBaseVersion:Int):String
{
var bestVersion = 0.0;
var result:String = null;
var ndkDir = defines.get("ANDROID_NDK_DIR");
if (ndkDir!=null)
{
Log.v("Looking in ANDROID_NDK_DIR " + ndkDir);
}
else
{
Log.v("ANDROID_NDK_DIR not set");
if (BuildTool.isMac)
{
var lib = defines.get("HOME") + "/Library/Android/sdk/ndk";
if (FileSystem.exists(lib))
{
Log.v("trying default " + lib);
ndkDir = lib;
}
}
}
if (ndkDir!=null)
{
ndkDir = ndkDir.split("\\").join("/");
var files:Array<String> = null;
var checkFiles:Bool = true;
try
{
files = FileSystem.readDirectory(ndkDir);
}
catch (e:Dynamic)
{
Log.warn('ANDROID_NDK_DIR "$ndkDir" does not point to a valid directory');
checkFiles=false;
}
if (checkFiles)
for (file in files)
{
file = file.split("\\").join("/");
var version = getNdkVersion(ndkDir + "/" + file);
if (inBaseVersion==0 || Std.int(version)==inBaseVersion)
{
if (version>bestVersion)
{
bestVersion = version;
result = ndkDir + "/" + file;
}
}
}
}
Log.v("Looking in ANDROID_SDK/ndk-bundle");
if (defines.exists("ANDROID_SDK"))
{
Log.v("checks default ndk-bundle in android sdk");
var ndkBundle = defines.get("ANDROID_SDK")+"/ndk-bundle";
ndkBundle = ndkBundle.split("\\").join("/");
var version = getNdkVersion(ndkBundle);
if (version>bestVersion && (inBaseVersion==0 || inBaseVersion==Std.int(version)) )
{
Log.v("Using default ndk-bundle in android sdk");
result = ndkBundle;
}
}
return result;
}
static public function getNdkVersion(inDirName:String):Float
{
Log.v("Try to get version from source.properties");
var src = toPath(inDirName+"/source.properties");
if (sys.FileSystem.exists(src))
{
var fin = sys.io.File.read(src, false);
try
{
while (true)
{
var str = fin.readLine();
var split = str.split ("=");
var name = StringTools.trim(split[0]);
if (name == "Pkg.Revision")
{
var revision = StringTools.trim(split[1]);
var split2 = revision.split( "." );
var result:Float = 1.0 * Std.parseInt(split2[0]) + 0.001 * Std.parseInt(split2[1]);
if (result>=8)
{
Log.v('Deduced NDK version '+result+' from "$inDirName"/source.properties');
fin.close();
return result;
}
}
}
}
catch (e:haxe.io.Eof)
{
Log.v('Could not deduce NDK version from "$inDirName"/source.properties');
}
fin.close();
}
var dir = inDirName.split("\\").join("/");
Log.v('Try to get version from directory name "$dir"');
var extract_version = ~/\/?(android-ndk-)?r(\d+)([a-z]?)$/;
if (extract_version.match(dir))
{
var major:Int = Std.parseInt( extract_version.matched(2) );
var result:Float = 1.0 * major;
var minor = extract_version.matched(3);
if (minor!=null && minor.length>0)
result += 0.001 * (minor.toLowerCase().charCodeAt(0)-'a'.code);
return result;
}
Log.v('Could not deduce NDK version from "$inDirName" - assuming 8');
return 8;
}
public static function initHXCPPConfig(ioDefines:Hash<String>)
{
var env = Sys.environment();
// If the user has set it themselves, they mush know what they are doing...
if (env.exists("HXCPP_CONFIG"))
return;
var home = "";
if (env.exists("HOME"))
home = env.get("HOME");
else if (env.exists("USERPROFILE"))
home = env.get("USERPROFILE");
else
{
Log.warn("No $HOME variable set, \".hxcpp_config.xml\" might be missing");
//Sys.println("Warning: No 'HOME' variable set - .hxcpp_config.xml might be missing.");
return;
}
ioDefines.set("HXCPP_HOME", home);
var config = toPath(home+"/.hxcpp_config.xml");
ioDefines.set("HXCPP_CONFIG",config);
if (BuildTool.HXCPP!="")
{
var src = toPath(BuildTool.HXCPP + "/toolchain/example.hxcpp_config.xml");
if (!sys.FileSystem.exists(config))
{
try
{
Log.info("", "Copying HXCPP config \"" + src + "\" to \"" + config + "\"");
sys.io.File.copy(src,config);
}
catch(e:Dynamic)
{
Log.warn("Could not create HXCPP config \"" + config + "\"");
//Sys.println("Warning : could not create config: " + config );
}
}
}
}
public static function setupMingw(ioDefines:Hash<String>)
{
// Setup MINGW_ROOT or fail
if (!ioDefines.exists("MINGW_ROOT"))
{
var haxelib = PathManager.getHaxelib("minimingw","",false);
if (haxelib!=null && haxelib!="")
{
ioDefines.set("MINGW_ROOT", haxelib);
Log.v('Using haxelib version of MinGW, $haxelib');
return;
}
var guesses = ["c:/MinGW"];
for (guess in guesses)
{
if (FileSystem.exists(guess))
{
ioDefines.set("MINGW_ROOT", guess);
Log.v('Using default version of MinGW, $guess');
return;
}
}
if (ioDefines.exists("mingw"))
{
//when mingw is explicitly indicated but not properly configured, this log will be shown
Log.error('Could not guess MINGW_ROOT (tried $guesses) - please set explicitly');
}
else
{
//when both mingw and MSVC is not properly configured, this log will be shown
Log.error('Could not setup any C++ compiler, please install or reinstall a valid C++ compiler');
}
}
}
public static function setupEmscripten(ioDefines:Hash<String>)
{
// Setup EMSCRIPTEN_SDK if possible - else assume developer has it in path
if (!ioDefines.exists("EMSCRIPTEN_SDK"))
{
var home = ioDefines.get("HXCPP_HOME");
var file = home + "/.emscripten";
if (FileSystem.exists(file))
{
var content = sys.io.File.getContent(file);
content = content.split("\r").join("");
var value = ~/^(\w*)\s*=\s*'(.*)'/;
for (line in content.split("\n"))
{
if (value.match(line))
{
var name = value.matched(1);
var val= value.matched(2);
if (name=="EMSCRIPTEN_ROOT")
{
ioDefines.set("EMSCRIPTEN_SDK", val);
}
if (name=="PYTHON")
ioDefines.set("EMSCRIPTEN_PYTHON", val);
if (name=="NODE_JS")
ioDefines.set("EMSCRIPTEN_NODE_JS", val);
}
}
}
}
}
public static function isRaspberryPi()
{
var modelFile = '/sys/firmware/devicetree/base/model';
if( !FileSystem.exists( modelFile ) )
return false;
try {
var model = sys.io.File.getContent( modelFile );
return ~/Raspberry/.match( model );
} catch(e:Dynamic) {
trace( e );
}
return false;
}
static public function startPdbServer()
{
var oldPath = Sys.getCwd();
try
{
// Run it in hxcpp directory so it does not lock the build directory after build finishes
Sys.setCwd(BuildTool.HXCPP);
var proc = new Process("mspdbsrv.exe",["-start"]);
Tools.addOnExitHook(function(_) {
proc.kill();
});
}
catch(e:Dynamic)
{
Log.v("Could not start mspdbsrv:" + e);
}
Sys.setCwd(oldPath);
}
public static function setup(inWhat:String,ioDefines:Map<String,String>)
{
if (ioDefines.exists("HXCPP_CLEAN_ONLY"))
return;
Profile.push("setup " + inWhat);
if (inWhat=="androidNdk")
{
setupAndroidNdk(ioDefines);
}
else if (inWhat=="blackberry")
{
setupBlackBerryNativeSDK(ioDefines);
}
else if (inWhat=="msvc")
{
setupMSVC(ioDefines, ioDefines.exists("HXCPP_M64"), ioDefines.exists("HXCPP_ARM64"), ioDefines.exists("winrt"));
}
else if (inWhat=="pdbserver")
{
startPdbServer();
}
else if (inWhat=="mingw")
{
setupMingw(ioDefines);
}
else if (inWhat=="emscripten")
{
setupEmscripten(ioDefines);
}
else if (inWhat=="nvcc")
{
BuildTool.setupNvcc();
}
else
{
Log.error('Unknown setup feature "$inWhat"');
//throw 'Unknown setup feature $inWhat';
}
Profile.pop();
}
static public function setupAndroidNdk(defines:Map<String,String>)
{
var root:String = null;
if (Log.verbose) Log.println("");
var ndkVersion = 0;
for (i in 6...20)
{
if (defines.exists("NDKV" + i))
{
ndkVersion = i;
Log.info("", "\x1b[33;1mRequested Android NDK r" + i + "\x1b[0m");
break;
}
}
if (!defines.exists("ANDROID_NDK_ROOT") || ndkVersion!=0)
{
root = Setup.findAndroidNdkRoot( defines, ndkVersion );
if (root==null)
{
var ndkDir = defines.exists("ANDROID_NDK_DIR") ? defines.get("ANDROID_NDK_DIR") : "not set";
if (ndkVersion!=0)
Log.error('ANDROID_NDK_DIR ["$ndkDir"] or ndk-bundle does not contain requested NDK $ndkVersion');
else
Log.error('ANDROID_NDK_DIR ["$ndkDir"] or ndk-bundle does not contain a matching NDK');
}
else
{
Sys.putEnv("ANDROID_NDK_ROOT", root);
defines.set("ANDROID_NDK_ROOT", root);
}
}
else
{
root = defines.get("ANDROID_NDK_ROOT");
Log.info("", "\x1b[33;1mUsing Android NDK root: " + root + "\x1b[0m");
}
if (ndkVersion==0)
{
var version = Setup.getNdkVersion( root );
if (version > 0)
{
Log.info("", "\x1b[33;1mDetected Android NDK " + version + "\x1b[0m");
defines.set("NDKV" + Std.int(version), "1" );
ndkVersion = Std.int(version);
}
else
Log.error("Invalid ndk version");
}
for (i in 5...ndkVersion+1)
defines.set("NDKV" + i + "+", "1");
var arm_type = 'arm-linux-androideabi';
var arm_64 = defines.exists('HXCPP_ARM64');
if(arm_64) arm_type = 'aarch64-linux-android';
// Find toolchain
if (!defines.exists("TOOLCHAIN_VERSION"))
{
try
{
var files = FileSystem.readDirectory(root+"/toolchains");
// Prefer clang?
var extract_version = ~/^arm-linux-androideabi-(\d.*)/;
if(arm_64) extract_version = ~/^aarch64-linux-android-(\d.*)/;
var bestVer="";
for (file in files)
{
if (extract_version.match(file))
{
var ver = extract_version.matched(1);
if (ver<bestVer || bestVer=="")
{
bestVer = ver;
}
}
}
if (bestVer!="")
{
defines.set("TOOLCHAIN_VERSION",bestVer);
Log.info("", "\x1b[33;1mDetected Android toolchain: "+arm_type+"-" + bestVer + "\x1b[0m");
}
}
catch(e:Dynamic) { }
}
// See what ANDROID_HOST to use ...
try
{
var prebuilt = root+"/toolchains/";
if (defines.exists("TOOLCHAIN_VERSION"))
prebuilt += arm_type + "-" + defines.get("TOOLCHAIN_VERSION") + "/prebuilt";
else
prebuilt += "llvm/prebuilt";
var files = FileSystem.readDirectory(prebuilt);
for (file in files)
{
if (!FileSystem.isDirectory (prebuilt + "/" + file))
{
files.remove (file);
}
}
if (files.length==1)
{
defines.set("ANDROID_HOST", files[0]);
Log.info("", "\x1b[33;1mDetected Android host: " + files[0] + "\x1b[0m");
}
else
{
Log.info("", "\x1b[33;1mCould not detect ANDROID_HOST (" + files + ") - using default\x1b[0m");
}
}
catch(e:Dynamic) { }
if(defines.exists('NDKV20+')) {
Log.v([
"x86 Platform: 16",
"arm Platform: 16",
"x86_64 Platform: 21",
"arm_64 Platform: 21",
"Frameworks should set the minSdkVersion for each APK to these values."
].join('\n'));
}
else {
globallySetThePlatform(root, defines);
}
}
private static function globallySetThePlatform(root:String, defines:Map<String,String>) {
var androidPlatform = 5;
if (!defines.exists("PLATFORM"))
{
for (i in 5...100)
{
var test = "android-" + i;
if (defines.exists(test))
{
defines.set("PLATFORM",test);
break;
}
}
}
if (defines.exists("PLATFORM"))
{
var platform = defines.get("PLATFORM");
var id = Std.parseInt( platform.substr("android-".length) );
if (id==0 || id==null)
Log.error('Badly formed android PLATFORM "$platform" - should be like android-123');
androidPlatform = id;
Log.info("", "\x1b[33;1mUsing Android NDK platform: " + defines.get("PLATFORM") + "\x1b[0m");
}
else
{
var base = root + "/platforms";
var best = 0;
try
{
for (file in FileSystem.readDirectory(base))
{
if (file.substr(0,8)=="android-")
{
var platform = Std.parseInt(file.substr(8));
if (platform>best)
best = platform;
}
}
} catch(e:Dynamic) {}
if (best==0)
{
Log.error("Could not detect Android API platforms in \"" + base + "\"");
//throw "Could not find platform in " + base;
}
Log.info("", "\x1b[33;1mUsing newest Android NDK platform: " + best + "\x1b[0m");
defines.set("PLATFORM", "android-" + best);
androidPlatform = best;
}
defines.set("ANDROID_PLATFORM_DEFINE", "HXCPP_ANDROID_PLATFORM=" + androidPlatform);
if (Log.verbose) Log.println("");
}
public static function setupBlackBerryNativeSDK(ioDefines:Hash<String>)
{
if (!ioDefines.exists ("BLACKBERRY_NDK_ROOT"))
{
Log.error("Could not find BLACKBERRY_NDK_ROOT variable");
}
var fileName = ioDefines.get ("BLACKBERRY_NDK_ROOT");
if (BuildTool.isWindows)
{
fileName += "\\bbndk-env.bat";
}
else
{
fileName += "/bbndk-env.sh";
}
if (FileSystem.exists (fileName))
{
var fin = sys.io.File.read(fileName, false);
try
{
while(true)
{
var str = fin.readLine();
var split = str.split ("=");
var name = StringTools.trim (split[0].substr (split[0].lastIndexOf (" ") + 1));
switch (name)
{
case "QNX_HOST", "QNX_TARGET", "QNX_HOST_VERSION", "QNX_TARGET_VERSION":
var value = split[1];
if (StringTools.startsWith (value, "${") && split.length > 2)
{
value = split[2].substr (0, split[2].length - 1);
}
if (StringTools.startsWith(value, "\""))
{
value = value.substr (1);
}
if (StringTools.endsWith(value, "\""))
{
value = value.substr (0, value.length - 1);
}
if (name == "QNX_HOST_VERSION" || name == "QNX_TARGET_VERSION")
{
if (Sys.getEnv (name) != null)
{
continue;
}
}
else
{
value = StringTools.replace (value, "$BASE_DIR", ioDefines.get ("BLACKBERRY_NDK_ROOT"));
value = StringTools.replace (value, "%BASE_DIR%", ioDefines.get ("BLACKBERRY_NDK_ROOT"));
value = StringTools.replace (value, "$TARGET", "qnx6");
value = StringTools.replace (value, "%TARGET%", "qnx6");
value = StringTools.replace (value, "$QNX_HOST_VERSION", Sys.getEnv("QNX_HOST_VERSION"));
value = StringTools.replace (value, "$QNX_TARGET_VERSION", Sys.getEnv("QNX_TARGET_VERSION"));
value = StringTools.replace (value, "%QNX_HOST_VERSION%", Sys.getEnv("QNX_HOST_VERSION"));
value = StringTools.replace (value, "%QNX_TARGET_VERSION%", Sys.getEnv("QNX_TARGET_VERSION"));
}
ioDefines.set(name,value);
Sys.putEnv(name,value);
}
}
}
catch( ex:Eof )
{}
fin.close();
}
else
{
Log.error("Could not find \"" + fileName + "\"");
}
}
public static function setupMSVC(ioDefines:Hash<String>, in64:Bool, inArm64, isWinRT:Bool)
{
var detectMsvc = !ioDefines.exists("NO_AUTO_MSVC") &&
!ioDefines.exists("HXCPP_MSVC_CUSTOM");
if (ioDefines.exists("HXCPP_MSVC_VER"))
{
var val = ioDefines.get("HXCPP_MSVC_VER");
if (val=="")
detectMsvc = false;
else
{
var ival = Std.parseInt(ioDefines.get("HXCPP_MSVC_VER"));
if (ival>0)
{
var varName = "VS" + ival+ "COMNTOOLS";
var where = Sys.getEnv(varName);
if (where==null)
{
for (env in Sys.environment().keys())
{
if (env.substr(0,2)=="VS")
{
Log.info("Found VS variable: " + env);
//Sys.println("Found VS variable " + env);
}
}
Log.error("Could not find specified MSVC version: " + ival);
//throw "Could not find specified MSVC version " + ival;
}
ioDefines.set("HXCPP_MSVC", where );
Sys.putEnv("HXCPP_MSVC", where);
Log.info("", 'Using MSVC Ver $ival in $where ($varName)');
}
else
{
Log.info("", 'Using specified MSVC Ver $val');
ioDefines.set("HXCPP_MSVC", val );
Sys.putEnv("HXCPP_MSVC", val);
}
}
}
if (detectMsvc)
{
var extra:String = "";
if (isWinRT)
extra += "-winrt";
if (inArm64)
extra += "-arm64";
else if (in64)
extra += "64";
var xpCompat = false;
if (ioDefines.exists("HXCPP_WINXP_COMPAT"))
{
Sys.putEnv("HXCPP_WINXP_COMPAT","1");
xpCompat = true;
}
Sys.putEnv("msvc_host_arch", ioDefines.exists("windows_arm_host") ? "x86" : "x64" );
var vc_setup_proc = new Process("cmd.exe", ["/C", BuildTool.HXCPP + "\\toolchain\\msvc" + extra + "-setup.bat" ]);
var vars_found = false;
var error_string = "";
var output = new Array<String>();
try{
while(true)
{
var str = vc_setup_proc.stdout.readLine();
if (str=="HXCPP_VARS")
vars_found = true;
else if (!vars_found)
{
if (str.substr(0,5)=="Error" || ~/missing/.match(str) )
error_string = str;
output.push(str);
}
else
{
var pos = str.indexOf("=");
var name = str.substr(0,pos);
switch(name.toLowerCase())
{
case "path", "vcinstalldir", "windowssdkdir","framework35version",
"frameworkdir", "frameworkdir32", "frameworkversion",
"frameworkversion32", "devenvdir", "include", "lib", "libpath", "hxcpp_xp_define",
"hxcpp_hack_pdbsrv"
:
var value = str.substr(pos+1);
ioDefines.set(name,value);
Log.v(' msvs $name=$value');
Sys.putEnv(name,value);
}
}
}
} catch (e:Dynamic) {
};
vc_setup_proc.exitCode();
vc_setup_proc.close();
if (!vars_found || error_string!="")
{
for (o in output)
{
Log.info(o);
//BuildTool.println(o);
}
if (error_string!="")
{
Log.error (error_string);
//throw(error_string);
}
else
{
Log.info("Missing HXCPP_VARS");
//BuildTool.println("Missing HXCPP_VARS");
}
Log.error("Could not automatically setup MSVC");
//throw("Could not automatically setup MSVC");
}
}
try
{
var proc = new Process("cl.exe",[]);
var str = proc.stderr.readLine();
proc.close();
if (str>"")
{
Log.v("MSVC output:" + str);
var reg = ~/(\d{2})\.\d+/i;
if (reg.match(str))
{
var cl_version = Std.parseInt(reg.matched(1));
Log.info("", "Using MSVC version: " + cl_version);
ioDefines.set("MSVC_VER", cl_version+"");
if (cl_version>=17)
ioDefines.set("MSVC17+","1");
if (cl_version>=18)
ioDefines.set("MSVC18+","1");
if (cl_version==19)
ioDefines.set("MSVC19","1");
BuildTool.sAllowNumProcs = cl_version >= 14;
var threads = BuildTool.getThreadCount();
if (threads>1 && cl_version>=18)
ioDefines.set("HXCPP_FORCE_PDB_SERVER","1");
}
}
} catch(e:Dynamic){}
//if (cl_version!="") BuildTool.println("Using cl version: " + cl_version);
}
static function toPath(inPath:String)
{
if (!BuildTool.isWindows)
return inPath;
var bits = inPath.split("/");
return bits.join("\\");
}
}