def main_bootstrap_build(args, input, state, output): try: print("") output.print_main_title() try_restore_previous_state(input, "n", state) target = select_target(input, state, output) Path("Build").mkdir(exist_ok=True) dependencies = Dependencies() dependencies.check(output) projects = Projects(target) projects.set_environment_variables(output) download_source_packages(projects, args.skip_downloads, input, state, output) compilers = Compilers(target) compiler = compilers.select_compiler(input, state, output) build_configuration = BuildConfiguration() build_configuration.select_configuration(target.architecture, compiler, input, state) cmake = CMake(compiler.cmake_generator) cmake.install(target, state, output) codesmithymake = CodeSmithyMake(target.architecture) build_tools = BuildTools(cmake, compiler, codesmithymake) projects.build(build_tools, build_configuration, input, state, output) print("") output.print_step_title("Running tests") if args.skip_tests: print(" Skipping tests") else: projects.test(compiler, build_configuration.architecture_dir_name, input) output.next_step() print("") output.print_step_title("Setting up second-phase of bootstrap") second_phase_path = str(Path(os.getcwd()).parent) + \ "/SecondPhaseBootstrap" Path(second_phase_path).mkdir(exist_ok=True) print(second_phase_path) # TODO shutil.copyfile("Build/CodeSmithyIDE/CodeSmithy/Bin/x64/CodeSmithy.exe", second_phase_path + "/CodeSmithy.exe") output.next_step() except RuntimeError as error: print("") print("ERROR:", error) sys.exit(-1)
def __init__(self, generatorInfo, rootDirectory, sourceDirectory, buildFolder): self.logger = logging.getLogger(__name__) self.cmake = CMake() self.generatorInfo = generatorInfo self.sourceDirectory = sourceDirectory self.rootDirectory = rootDirectory self.buildFolder = buildFolder
def __init__(self, buildExecutor, generatorInfo, sourceDirectory, buildFolder): self.logger = logging.getLogger(__name__) self.generatorInfo = generatorInfo self.sourceDirectory = sourceDirectory self.buildFolder = buildFolder self.buildExecutor = buildExecutor self.gradle = Gradle(sourceDirectory) self.cmake = CMake() self.androidBuildApiVersion = "28" self.androidBuildToolsVersion = "28.0.2" self.androidEmulatorApiVersion = "28"
def match_command_groups(contents, base_depth=0): revised_contents = [] current = [] group = None depth = base_depth for content in contents: if group is None: if content.__class__ == Command and content.command_name in ['if', 'foreach']: group = content depth = base_depth + 1 else: revised_contents.append(content) else: if content.__class__ == Command: if content.command_name == group.command_name: depth += 1 elif content.command_name == 'end' + group.command_name: depth -= 1 if depth == base_depth: recursive_contents = match_command_groups(current, base_depth + 1) sub = CMake(initial_contents=recursive_contents, depth=base_depth + 1) cg = CommandGroup(group, sub, content) revised_contents.append(cg) group = None current = [] continue current.append(content) # Only will happen if the tags don't match. Shouldn't happen, but resolve leftovers if len(current) > 0: revised_contents += current return revised_contents
class AndroidExecutor: def __init__(self, buildExecutor, generatorInfo, sourceDirectory, buildFolder, rootPath): self.logger = logging.getLogger(__name__) self.generatorInfo = generatorInfo self.sourceDirectory = sourceDirectory self.buildFolder = buildFolder self.buildExecutor = buildExecutor self.gradle = Gradle(sourceDirectory) self.rootPath = rootPath self.cmake = CMake() self.androidBuildApiVersion = "28" self.androidBuildToolsVersion = "28.0.2" self.androidEmulatorApiVersion = "28" def buildTarget(self, configuration, args, target): androidAbi = self.getAndroidABIFromArch(configuration.arch) androidHome = self.getAndroidHome() buildDir = self.buildFolder.getBuildDir(configuration) if configuration.buildsystem == "AndroidStudio": self.buildTargetAndroidStudio(configuration, args, target, androidAbi, androidHome, buildDir) else: self.buildTargetMake(configuration, args, target) def buildTargetMake(self, configuration, args, target): buildExecutor = BuildExecutor(self.generatorInfo, self.rootPath, self.sourceDirectory, self.buildFolder) buildExecutor.buildTarget(configuration, args, target) def buildTargetAndroidStudio(self, configuration, args, target, androidAbi, androidHome, buildDir): gradlePath = self.gradle.getGradlePath() gradleWrapperPath = self.getBuildToolPath(buildDir, "gradlew") arguments = ["\"" + gradleWrapperPath + "\""] if target == "clean": arguments += ["clean"] else: if args.config == "Release": arguments += ["assembleRelease"] else: arguments += ["assembleDebug"] self.logger.debug("Starting: %s", arguments) exitCode = subprocess.call(" ".join(arguments), shell=True, cwd=buildDir, env=self.getToolEnv()) if exitCode != 0: raise error.ToolFailedError("%s" % (arguments), exitCode) def build(self, configuration, args): self.buildTarget(configuration, args, None) def clean(self, configuration, args): self.buildTarget(configuration, args, "clean") def package(self, configuration, args): if configuration.buildsystem == "AndroidStudio": self.logger.critical( "Cannot build packages with Android Studio, use make instead") else: self.buildTarget(configuration, args, "package") def prepare(self, platformState, configuration, args): androidAbi = self.getAndroidABIFromArch(configuration.arch) androidHome = self.getAndroidHome() self.prepareAndroidEnvironment(configuration, args.accept_terms) buildDir = self.buildFolder.getBuildDir(configuration) if configuration.buildsystem == "AndroidStudio": self.prepareAndroidStudio(platformState, configuration, androidAbi, androidHome, buildDir, args) else: self.prepareMake(platformState, configuration, args, androidAbi, androidHome, buildDir) def prepareMake(self, platformState, configuration, args, androidAbi, androidHome, cmakeBuildDir): self.cmake.open(self.sourceDirectory, cmakeBuildDir, "Unix Makefiles") android_abi_arg = self.getAndroidABIFromArch(configuration.arch) if not android_abi_arg: raise error.InvalidArchitectureError( "No target architecture specified. The architecture parameter is required for makefile build systems." ) cmakeArguments = [ "-DCMAKE_TOOLCHAIN_FILE=%s/ndk-bundle/build/cmake/android.toolchain.cmake" % (androidHome), "-DANDROID_ABI=%s" % (android_abi_arg), "-DANDROID_NATIVE_API_LEVEL=%s" % (self.androidBuildApiVersion), "-DCMAKE_BUILD_TYPE=%s" % (configuration.config), "-DBDN_BUILD_TESTS=Off", "-DBDN_BUILD_EXAMPLES=Off", ] if args.cmake_option: for option in args.cmake_option: cmakeArguments += ["-D" + option] if args.package_generator: cmakeArguments += [ "-DCPACK_GENERATOR=%s" % (args.package_generator) ] if args.package_folder: packageFolder = args.package_folder if not os.path.isabs(packageFolder): packageFolder = os.path.join( self.buildFolder.getBaseBuildDir(), packageFolder) cmakeArguments += [ "-DCPACK_OUTPUT_FILE_PREFIX=%s" % (packageFolder) ] self.logger.warning( "Disabling examples and tests, as we cannot build apk's yet.") self.logger.debug("Starting configure ...") self.logger.debug(" Source Directory: %s", self.sourceDirectory) self.logger.debug(" Output Directory: %s", cmakeBuildDir) self.logger.debug(" Config: %s", configuration.config) self.logger.debug(" Arguments: %s", cmakeArguments) self.logger.debug(" Generator: %s", "Unix Makefiles") self.cmake.configure(cmakeArguments) pass def prepareAndroidStudio(self, platformState, configuration, androidAbi, androidHome, buildDir, args): gradlePath = self.gradle.getGradlePath() self.gradle.stop() tmpCMakeFolder = os.path.join(buildDir, "tmp-cmake-gen") makefile_android_abi = self.getAndroidABIFromArch(configuration.arch) if not makefile_android_abi: # If we target multiple architectures at the same time, we simply use x86 for the temporary # Makefile project we generate here (since makefiles support only one architecture). # Note that the makefile is only used temporarily to detect the available projects and is never # used to build anything. makefile_android_abi = "x86" self.cmake.open(self.sourceDirectory, tmpCMakeFolder, "Unix Makefiles") cmakeArguments = [ "-DCMAKE_TOOLCHAIN_FILE=%s/ndk-bundle/build/cmake/android.toolchain.cmake" % (androidHome), "-DCMAKE_SYSTEM_NAME=Android", "-DANDROID_ABI=%s" % (makefile_android_abi), "-DANDROID_NATIVE_API_LEVEL=%s" % (self.androidBuildApiVersion), "-DBAUER_RUN=Yes" ] if sys.platform == 'win32': cmakeArguments += [ "-DCMAKE_MAKE_PROGRAM=%s/ndk-bundle/prebuilt/windows-x86_64/bin/make.exe" % (androidHome) ] if args.cmake_option: for option in args.cmake_option: cmakeArguments += ["-D" + option] self.logger.debug("Starting configure ...") self.logger.debug(" Arguments: %s", cmakeArguments) self.logger.debug(" Generator: %s", "Unix Makefiles") self.cmake.configure(cmakeArguments) cmakeConfigurations = self.cmake.codeModel["configurations"] if len(cmakeConfigurations) != 1: raise Exception("Number of configurations is not 1!") target_dependencies = self.calculateDependencies(self.cmake.codeModel) config = cmakeConfigurations[0] project = config["main-project"] self.logger.debug("Found project: %s", project["name"]) targetNames = [] targets = [] for target in project["targets"]: if target["type"] == "SHARED_LIBRARY" or target[ "type"] == "EXECUTABLE" or target[ "type"] == "STATIC_LIBRARY": self.logger.debug("Found target: %s", target["name"]) targetNames += [target["name"]] targets += [target] project = { "name": project["name"], "sourceDirectory": project["sourceDirectory"], "targetNames": targetNames, "targets": targets } # Use external CMake for building native code (supported as of AndroidStudio 3.2) generator = AndroidStudioProjectGenerator(self.gradle, self.cmake, buildDir, self.androidBuildApiVersion) generator.generate(project, androidAbi, target_dependencies, args) def getToolEnv(self): toolEnv = os.environ toolEnv["ANDROID_HOME"] = self.getAndroidHome() return toolEnv def getAndroidHome(self): android_home_dir = os.environ.get("ANDROID_HOME") if not android_home_dir: if sys.platform.startswith("linux"): android_home_dir = os.path.expanduser("~/Android/Sdk") if os.path.exists(android_home_dir): self.logger.info( "Android home directory automatically detected as: %s", android_home_dir) else: android_home_dir = None if sys.platform == "darwin": android_home_dir = os.path.expanduser("~/Library/Android/sdk") if os.path.exists(android_home_dir): self.logger.info( "Android home directory automatically detected as: %s", android_home_dir) else: android_home_dir = None if sys.platform == "win32": android_home_dir = os.path.expanduser( "~/AppData/Local/Android/SDK") if os.path.exists(android_home_dir): self.logger.info( "Android home directory automatically detected as: %s", android_home_dir) else: android_home_dir = None if not android_home_dir: raise Exception( "ANDROID_HOME environment variable is not set. Please point it to the root of the android SDK installation." ) return android_home_dir.replace('\\', '/') def getAndroidABIFromArch(self, arch): if arch == "std": return None else: return arch def getAndroidABI(self, configuration): return self.getAndroidABIFromArch(configuration.arch) def getBuildToolPath(self, androidHome, tool): path = os.path.join(androidHome, tool) if sys.platform == "win32": if os.path.exists(path + ".bat"): path += ".bat" elif os.path.exists(path + ".exe"): path += ".exe" if not os.path.exists(path): raise Exception("Couldn't find %s" % (tool)) return path def calculateDependencies(self, codeModel): cmakeConfigurations = codeModel["configurations"] if len(cmakeConfigurations) != 1: raise Exception("Number of configurations is not 1!") config = cmakeConfigurations[0] projects = [] artifacts = {} for project in config["projects"]: for target in project["targets"]: if "artifacts" in target and target[ "type"] == "SHARED_LIBRARY" or target[ "type"] == "STATIC_LIBRARY": artifacts[target["name"]] = [] for artifact in target["artifacts"]: artifacts[target["name"]] += [ os.path.basename(artifact) ] dependencies = {} for project in config["projects"]: for target in project["targets"]: dependencies[target["name"]] = [] if "linkLibraries" in target: for depname, artifactList in artifacts.items(): for artifact in artifactList: if artifact in target["linkLibraries"]: dependencies[target["name"]] += [depname] return dependencies def tryDetectAndroidCmakeComponentName(self, sdkManagerPath): command = '"%s" --list' % (sdkManagerPath) try: output = subprocess.check_output(command, shell=True, env=self.getToolEnv(), universal_newlines=True) except: self.logger.warning("Failed to get Android SDK module list") return None last_cmake_component_name = None for line in output.splitlines(): line = line.strip() if line.startswith("cmake;"): last_cmake_component_name = line.partition(" ")[0] break return last_cmake_component_name def workaroundPlatformTools2903(self): try: androidHome = self.getAndroidHome() sdkManagerPath = self.getBuildToolPath(androidHome, "tools/bin/sdkmanager") sdkManagerCommand = '"%s" --list' % (sdkManagerPath) output = subprocess.check_output(sdkManagerCommand, shell=True, env=self.getToolEnv()) for line in output.splitlines(): parts = line.decode('utf-8').split('|') if len(parts) == 3: if parts[0].strip() == 'platform-tools': version = parts[1].strip() if version == '29.0.3': self.logger.info( "Since platform-tools 29.0.3 breaks debugging native apps, we manually download 29.0.2" ) tf = tempfile.NamedTemporaryFile(mode='w+b', delete=False) sourceName = "https://dl.google.com/android/repository/platform-tools_r29.0.2-" if "linux" in sys.platform: sourceName += "linux" elif sys.platform == "win32": sourceName += "windows" elif sys.platform == "darwin": sourceName += "darwin" sourceName += ".zip" download_file(sourceName, tf.name) zipf = MyZipFile(tf.name, 'r') zipf.extractall(androidHome) zipf.close() return True return False except: return False def prepareAndroidEnvironment(self, configuration, accept_terms): self.logger.info("Preparing android environment...") androidAbi = self.getAndroidABIFromArch(configuration.arch) androidHome = self.getAndroidHome() sdkManagerPath = self.getBuildToolPath(androidHome, "tools/bin/sdkmanager") if accept_terms: self.logger.info( "Ensuring that all android license agreements are accepted ..." ) licenseCall = subprocess.Popen('"%s" --licenses' % sdkManagerPath, shell=True, env=self.getToolEnv(), stdin=subprocess.PIPE) licenseInputData = "" for i in range(100): licenseInputData += "y\n" licenseCall.communicate(licenseInputData.encode('utf-8')) self.logger.info("Done updating licenses.") self.logger.info( "Ensuring that all necessary android packages are installed...") platformToolsPackageName = '"platform-tools"' if self.workaroundPlatformTools2903(): platformToolsPackageName = "" sdkManagerCommand = '"%s" %s "ndk-bundle" "extras;android;m2repository" "extras;google;m2repository" "build-tools;%s" "platforms;android-%s"' % ( sdkManagerPath, platformToolsPackageName, self.androidBuildToolsVersion, self.androidBuildApiVersion) cmakeComponentName = self.tryDetectAndroidCmakeComponentName( sdkManagerPath) if cmakeComponentName: sdkManagerCommand += ' "%s"' % cmakeComponentName try: subprocess.check_call(sdkManagerCommand, shell=True, env=self.getToolEnv()) except: self.logger.warning( "Failed getting emulator, you will not be able to 'run' this configuration" ) self.logger.info("Done updating packages.")
class BuildExecutor: def __init__(self, generatorInfo, rootDirectory, sourceDirectory, buildFolder): self.logger = logging.getLogger(__name__) self.cmake = CMake() self.generatorInfo = generatorInfo self.sourceDirectory = sourceDirectory self.rootDirectory = rootDirectory self.buildFolder = buildFolder def build(self, configuration, args): self.buildTarget(configuration, args, args.target) def clean(self, configuration, args): self.buildTarget(configuration, args, "clean") def package(self, configuration, args): self.buildTarget(configuration, args, "package") def buildTarget(self, configuration, args, target): configs = [configuration.config] if args.config != None: configs = [args.config] if target == None and args.target != None: target = args.target isSingleConfigBuildSystem = self.generatorInfo.isSingleConfigBuildSystem( configuration.buildsystem) if not isSingleConfigBuildSystem and args.config == None: configs = [] for cmakeConfiguration in self.cmake.codeModel["configurations"]: configs += [cmakeConfiguration["name"]] for config in configs: buildDirectory = self.buildFolder.getBuildDir(configuration) commandArguments = [ "\"%s\"" % self.cmake.cmakeExecutable, "--build", "\"%s\"" % buildDirectory ] if target: commandArguments += ["--target", target] if not isSingleConfigBuildSystem: commandArguments += ["--config", config] if args.jobs != None: generatorName = self.generatorInfo.getCMakeGeneratorName( configuration.buildsystem) if "Visual Studio" in generatorName: os.environ["CL"] = "/MP" + args.jobs elif "Xcode" in generatorName: pass # Can' specify multicore via command line :( else: commandArguments += ["--", "-j%s" % (args.jobs)] commandLine = " ".join(commandArguments) self.logger.info("Calling: %s", commandLine) exitCode = subprocess.call(commandLine, shell=True, cwd=buildDirectory) if exitCode != 0: raise error.ToolFailedError(commandLine, exitCode) def prepare(self, platformState, configuration, args): self.logger.debug("prepare(%s)", configuration) cmakeBuildDir = self.buildFolder.getBuildDir(configuration) toolChainFileName = None needsCleanBuildDir = False cmakeEnvironment = "" cmakeArch = configuration.arch cmakeArguments = [] self.generatorInfo.ensureHaveCmake() generatorName = self.generatorInfo.getCMakeGeneratorName( configuration.buildsystem) commandIsInQuote = False if args.package_generator: cmakeArguments += [ "-DCPACK_GENERATOR=%s" % (args.package_generator) ] if args.package_folder: packageFolder = args.package_folder if not os.path.isabs(packageFolder): packageFolder = os.path.join( self.buildFolder.getBaseBuildDir(), packageFolder) cmakeArguments += [ "-DCPACK_OUTPUT_FILE_PREFIX=%s" % (packageFolder) ] if configuration.platform == "mac": if configuration.arch != "std": raise error.InvalidArchitectureError(arch) if args.macos_sdk_path: cmakeArguments.extend( ["-DCMAKE_OSX_SYSROOT=%s" % (args.macos_sdk_path)]) if args.macos_min_version: cmakeArguments.extend([ "-DCMAKE_OSX_DEPLOYMENT_TARGET=%s" % (args.macos_min_version) ]) elif configuration.platform == "ios": if generatorName == 'Xcode' and configuration.arch == "std": toolChainFileName = "ios.xcode.toolchain.cmake" xcodeDevPath = subprocess.check_output( ['xcode-select', '-p']).decode("utf-8").strip() whichCxx = subprocess.check_output(['which', 'c++' ]).decode("utf-8").strip() self.logger.debug("Environment info:") self.logger.debug('\t"xcode-select -p": "%s"' % (xcodeDevPath)) self.logger.debug('\t"which c++": "%s"' % (whichCxx)) expected_clangxx_location = os.path.join( xcodeDevPath, "Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++") if not os.path.isfile(expected_clangxx_location): self.logger.warning( 'Expected file "%s" to exist but it did not' % (expected_clangxx_location)) else: if configuration.arch == "std" or configuration.arch == "simulator": cmakeArguments.extend(["-DIOS_PLATFORM=SIMULATOR64"]) elif configuration.arch == "device": cmakeArguments.extend(["-DIOS_PLATFORM=OS64"]) else: raise error.InvalidArchitectureError(arch) toolChainFileName = "ios.make.toolchain.cmake" if toolChainFileName: toolChainFilePath = os.path.join(self.rootDirectory, "cmake/toolchains", toolChainFileName) if not os.path.isfile(toolChainFilePath): self.logger.error( "Required CMake toolchain file not found: %s", toolChainFilePath) return 5 cmakeArguments += ["-DCMAKE_TOOLCHAIN_FILE=" + toolChainFilePath] if configuration.config: cmakeArguments += ["-DCMAKE_BUILD_TYPE=" + configuration.config] if cmakeArch: cmakeArguments += ["-A " + cmakeArch] if args.cmake_option: for option in args.cmake_option: cmakeArguments += ["-D" + option] if needsCleanBuildDir: shutil.rmtree(cmakeBuildDir) self.cmake.open(self.sourceDirectory, cmakeBuildDir, generatorName, extraGeneratorName="", extraEnv=cmakeEnvironment) self.logger.debug("Starting configure ...") self.logger.debug(" Source Directory: %s", self.sourceDirectory) self.logger.debug(" Output Directory: %s", cmakeBuildDir) self.logger.debug(" Arguments: %s", cmakeArguments) self.logger.debug(" Generator: %s", generatorName) self.cmake.configure(cmakeArguments)
def init(self): """ this method is called right after reading the configuration file. so, it is useful for initializing variables that depend on the installation mode or anything that depends on user changes in the configuration file """ if (os.system("which which > /dev/null 2>&1") != 0): print "\"which\" is not installed on your system!!" sys.exit(1) t = time.gmtime() # save installation time to variable self.time = str(t[0]) + "-" + str(t[1]) + "-" + str(t[2]) + "_" + str( t[3]) + "-" + str(t[4]) self.ctime = time.ctime() self.logfile = self.logsdir + self.config_file_prefix + "-" + self.time + ".log" # auto detect modules if (self.module("Java") == None): auto_java = Java() if (auto_java.autoDetected): self.autoModules.append(auto_java) if (self.module("QT") == None): auto_qt = QT() if (auto_qt.autoDetected): self.autoModules.append(auto_qt) if (self.module("CMake") == None): auto_cmake = CMake() if (auto_cmake.autoDetected): self.autoModules.append(auto_cmake) # initialize cmake supported modules for mod in self.modules: if (mod.hasCMakeFindSupport): self.cmakeSupportedMods.append(mod.name) # initialize each module print "+ Initialize modules..." for mod in self.modules: mod.init() print "\n+ Check for previous installations...\n" for mod in self.modules: mod.checkInstallConsistency() # skip dependencies for downloading only if (self.downloadOnly): return print "\n+ Dependencies Pre-Check..." for mod in self.modules: mod.preCheckDeps() print "\n+ Dependencies Check..." PkgUpdated = True while PkgUpdated: PkgUpdated = False for mod in self.modules: mod.checkRequiredDependencies() for mod in self.modules: mod.checkOptionalDependencies() for mod in self.modules: if (not mod.checkDeps()): PkgUpdated = True print "\n+ Dependencies Post-Check..." for mod in self.modules: mod.postCheckDeps() print
class AndroidExecutor: def __init__(self, buildExecutor, generatorInfo, sourceDirectory, buildFolder): self.logger = logging.getLogger(__name__) self.generatorInfo = generatorInfo self.sourceDirectory = sourceDirectory self.buildFolder = buildFolder self.buildExecutor = buildExecutor self.gradle = Gradle(sourceDirectory) self.cmake = CMake() self.androidBuildApiVersion = "28" self.androidBuildToolsVersion = "28.0.2" self.androidEmulatorApiVersion = "28" def buildTarget(self, configuration, args, target): androidAbi = self.getAndroidABIFromArch(configuration.arch) androidHome = self.getAndroidHome() buildDir = self.buildFolder.getBuildDir(configuration) if configuration.buildsystem == "AndroidStudio": self.buildTargetAndroidStudio(configuration, args, target, androidAbi, androidHome, buildDir) else: self.buildTargetMake(configuration, args, target) def buildTargetMake(self, configuration, args, target): buildExecutor = BuildExecutor(self.generatorInfo, self.sourceDirectory, self.buildFolder) buildExecutor.buildTarget(configuration, args, target) def buildTargetAndroidStudio(self, configuration, args, target, androidAbi, androidHome, buildDir): gradlePath = self.gradle.getGradlePath() gradleWrapperPath = self.getBuildToolPath(buildDir, "gradlew") arguments = ["\"" + gradleWrapperPath + "\""] if target == "clean": arguments += ["clean"] else: if args.config == "Release": arguments += ["assembleRelease"] else: arguments += ["assembleDebug"] self.logger.debug("Starting: %s", arguments) exitCode = subprocess.call(" ".join(arguments), shell=True, cwd=buildDir, env=self.getToolEnv()) if exitCode != 0: raise error.ToolFailedError("%s" % (arguments), exitCode) def build(self, configuration, args): self.buildTarget(configuration, args, None) def clean(self, configuration, args): self.buildTarget(configuration, args, "clean") def package(self, configuration, args): if configuration.buildsystem == "AndroidStudio": self.logger.critical( "Cannot build packages with Android Studio, use make instead") else: self.buildTarget(configuration, args, "package") def prepare(self, platformState, configuration, args): androidAbi = self.getAndroidABIFromArch(configuration.arch) androidHome = self.getAndroidHome() self.prepareAndroidEnvironment(configuration, args.accept_terms) buildDir = self.buildFolder.getBuildDir(configuration) if configuration.buildsystem == "AndroidStudio": self.prepareAndroidStudio(platformState, configuration, androidAbi, androidHome, buildDir) else: self.prepareMake(platformState, configuration, args, androidAbi, androidHome, buildDir) def prepareMake(self, platformState, configuration, args, androidAbi, androidHome, cmakeBuildDir): self.cmake.open(self.sourceDirectory, cmakeBuildDir, "Unix Makefiles") android_abi_arg = self.getAndroidABIFromArch(configuration.arch) if not android_abi_arg: raise error.InvalidArchitectureError( "No target architecture specified. The architecture parameter is required for makefile build systems." ) cmakeArguments = [ "-DCMAKE_TOOLCHAIN_FILE=%s/ndk-bundle/build/cmake/android.toolchain.cmake" % (androidHome), "-DANDROID_ABI=%s" % (android_abi_arg), "-DANDROID_NATIVE_API_LEVEL=%s" % (self.androidBuildApiVersion), "-DCMAKE_BUILD_TYPE=%s" % (configuration.config), "-DBDN_BUILD_TESTS=Off", "-DBDN_BUILD_EXAMPLES=Off", ] if args.package_generator: cmakeArguments += [ "-DCPACK_GENERATOR=%s" % (args.package_generator) ] if args.package_folder: packageFolder = args.package_folder if not os.path.isabs(packageFolder): packageFolder = os.path.join( self.buildFolder.getBaseBuildDir(), packageFolder) cmakeArguments += [ "-DCPACK_OUTPUT_FILE_PREFIX=%s" % (packageFolder) ] self.logger.warning( "Disabling examples and tests, as we cannot build apk's yet.") self.logger.debug("Starting configure ...") self.logger.debug(" Source Directory: %s", self.sourceDirectory) self.logger.debug(" Output Directory: %s", cmakeBuildDir) self.logger.debug(" Config: %s", configuration.config) self.logger.debug(" Arguments: %s", cmakeArguments) self.logger.debug(" Generator: %s", "Unix Makefiles") self.cmake.configure(cmakeArguments) pass def prepareAndroidStudio(self, platformState, configuration, androidAbi, androidHome, buildDir): gradlePath = self.gradle.getGradlePath() self.gradle.stop() tmpCMakeFolder = os.path.join(buildDir, "tmp-cmake-gen") makefile_android_abi = self.getAndroidABIFromArch(configuration.arch) if not makefile_android_abi: # If we target multiple architectures at the same time then we simply use x86 for the temporary # Makefile project we generate here (since makefiles support only one architecture). # Note that the makefile is only used temporarily to detect the available projects and is never # used to build anything. makefile_android_abi = "x86" self.cmake.open(self.sourceDirectory, tmpCMakeFolder, "Unix Makefiles") cmakeArguments = [ "-DCMAKE_TOOLCHAIN_FILE=%s/ndk-bundle/build/cmake/android.toolchain.cmake" % (androidHome), "-DCMAKE_SYSTEM_NAME=Android", "-DANDROID_ABI=%s" % (makefile_android_abi), "-DANDROID_NATIVE_API_LEVEL=%s" % (self.androidBuildApiVersion), "-DBAUER_RUN=Yes" ] if sys.platform == 'win32': cmakeArguments += [ "-DCMAKE_MAKE_PROGRAM=%s/ndk-bundle/prebuilt/windows-x86_64/bin/make.exe" % (androidHome) ] self.logger.debug("Starting configure ...") self.logger.debug(" Arguments: %s", cmakeArguments) self.logger.debug(" Generator: %s", "Unix Makefiles") self.cmake.configure(cmakeArguments) cmakeConfigurations = self.cmake.codeModel["configurations"] if len(cmakeConfigurations) != 1: raise Exception("Number of configurations is not 1!") targetDependencies = self.calculateDependencies(self.cmake.codeModel) config = cmakeConfigurations[0] projects = [] for project in config["projects"]: self.logger.debug("Found project: %s", project["name"]) #projects += [project] targetNames = [] targets = [] for target in project["targets"]: if target["type"] == "SHARED_LIBRARY" or target[ "type"] == "EXECUTABLE" or target[ "type"] == "STATIC_LIBRARY": self.logger.debug("Found target: %s", target["name"]) targetNames += [target["name"]] targets += [target] projects += [{ "name": project["name"], "sourceDirectory": project["sourceDirectory"], "targetNames": targetNames, "targets": targets }] # The way Android Studio builds projects with native code has changed # quite a bit over time. It used to be very messy, requiring experimental # plugins to work properly. # Since AndroidStudio 3.0 this has changed and there is a "standard" way # to do this. AndroidStudio now includes a custom, forked CMake # that actually works - and this builds the native code side. # We only support the new style projects now (see git history if # you want to know what it used to look like). generator = AndroidStudioProjectGenerator(self.gradle, buildDir, self.androidBuildApiVersion) for project in projects: self.logger.debug("Generating project %s with targets: %s", project["name"], project["targetNames"]) generator.generateTopLevelProject(project["targetNames"]) for target in project["targets"]: targetName = target["name"] rootCMakeFile = os.path.join(project["sourceDirectory"], "CMakeLists.txt").replace( '\\', '/') generator.generateModule( packageId="io.boden.android.%s" % (targetName), cmakeTargetName=targetName, targetSourceDirectory=target["sourceDirectory"], userFriendlyTargetName=targetName, dependencyList=targetDependencies[targetName], isLibrary=target["type"] == "SHARED_LIBRARY", android_abi=androidAbi, rootCMakeFile=rootCMakeFile) break # At the time of this writing, Android Studio will not detect when new source files # have been added (even if we do a gradle sync, syncs on the cmake file, etc.). # To force re-detection of that we delete the .idea folder. idea_dir = os.path.join(buildDir, ".idea") if os.path.exists(idea_dir): self.logger.info( "Deleting .idea folder in build dir to force re-detection of files." ) shutil.rmtree(idea_dir) def getToolEnv(self): toolEnv = os.environ toolEnv["ANDROID_HOME"] = self.getAndroidHome() return toolEnv def getAndroidHome(self): android_home_dir = os.environ.get("ANDROID_HOME") if not android_home_dir: if sys.platform.startswith("linux"): android_home_dir = os.path.expanduser("~/Android/Sdk") if os.path.exists(android_home_dir): self.logger.info( "Android home directory automatically detected as: %s", android_home_dir) else: android_home_dir = None if sys.platform == "darwin": android_home_dir = os.path.expanduser("~/Library/Android/sdk") if os.path.exists(android_home_dir): self.logger.info( "Android home directory automatically detected as: %s", android_home_dir) else: android_home_dir = None if sys.platform == "win32": android_home_dir = os.path.expanduser( "~/AppData/Local/Android/SDK") if os.path.exists(android_home_dir): self.logger.info( "Android home directory automatically detected as: %s", android_home_dir) else: android_home_dir = None if not android_home_dir: raise Exception( "ANDROID_HOME environment variable is not set. Please point it to the root of the android SDK installation." ) return android_home_dir.replace('\\', '/') def getAndroidABIFromArch(self, arch): if arch == "std": return None else: return arch def getAndroidABI(self, configuration): return self.getAndroidABIFromArch(configuration.arch) def getBuildToolPath(self, androidHome, tool): path = os.path.join(androidHome, tool) if sys.platform == "win32": if os.path.exists(path + ".bat"): path += ".bat" elif os.path.exists(path + ".exe"): path += ".exe" if not os.path.exists(path): raise Exception("Couldn't find %s" % (tool)) return path def calculateDependencies(self, codeModel): cmakeConfigurations = codeModel["configurations"] if len(cmakeConfigurations) != 1: raise Exception("Number of configurations is not 1!") config = cmakeConfigurations[0] projects = [] artifacts = {} for project in config["projects"]: for target in project["targets"]: if "artifacts" in target and target[ "type"] == "SHARED_LIBRARY" or target[ "type"] == "STATIC_LIBRARY": artifacts[target["name"]] = [] for artifact in target["artifacts"]: artifacts[target["name"]] += [ os.path.basename(artifact) ] dependencies = {} for project in config["projects"]: for target in project["targets"]: dependencies[target["name"]] = [] if "linkLibraries" in target: for depname, artifactList in artifacts.items(): for artifact in artifactList: if artifact in target["linkLibraries"]: dependencies[target["name"]] += [depname] return dependencies def tryDetectAndroidCmakeComponentName(self, sdkManagerPath): command = '"%s" --list' % (sdkManagerPath) try: output = subprocess.check_output(command, shell=True, env=self.getToolEnv(), universal_newlines=True) except: self.logger.warning("Failed to get Android SDK module list") return None last_cmake_component_name = None for line in output.splitlines(): line = line.strip() if line.startswith("cmake;"): last_cmake_component_name = line.partition(" ")[0] return last_cmake_component_name def prepareAndroidEnvironment(self, configuration, accept_terms): self.logger.info("Preparing android environment...") androidAbi = self.getAndroidABIFromArch(configuration.arch) androidHome = self.getAndroidHome() sdkManagerPath = self.getBuildToolPath(androidHome, "tools/bin/sdkmanager") if accept_terms: self.logger.info( "Ensuring that all android license agreements are accepted ..." ) licenseCall = subprocess.Popen('"%s" --licenses' % sdkManagerPath, shell=True, env=self.getToolEnv(), stdin=subprocess.PIPE) licenseInputData = "" for i in range(100): licenseInputData += "y\n" licenseCall.communicate(licenseInputData.encode('utf-8')) self.logger.info("Done updating licenses.") self.logger.info( "Ensuring that all necessary android packages are installed...") sdkManagerCommand = '"%s" "platform-tools" "ndk-bundle" "extras;android;m2repository" "extras;google;m2repository" "build-tools;%s" "platforms;android-%s"' % ( sdkManagerPath, self.androidBuildToolsVersion, self.androidBuildApiVersion) cmakeComponentName = self.tryDetectAndroidCmakeComponentName( sdkManagerPath) if cmakeComponentName: sdkManagerCommand += ' "%s"' % cmakeComponentName try: subprocess.check_call(sdkManagerCommand, shell=True, env=self.getToolEnv()) except: self.logger.warning( "Failed getting emulator, you will not be able to 'run' this configuration" ) self.logger.info("Done updating packages.")
#!/usr/bin/python import os import sys from cmake import CMake if len(sys.argv) < 2: print "usage: pycmake <srcdir>" sys.exit(0) cmake = CMake(sys.argv[1], '.') cmake.generate()
def parse_file(filename): with open(filename) as f: s = f.read() return CMake(file_path=filename, initial_contents=parse_commands(s))
def parse_argument(): """ Main script : define arguments and send to convertdata. """ data = { 'vcxproj': None, 'cmake': None, 'additional_code': None, 'includes': None, 'dependencies': None, 'cmake_output': None, 'data': None } # Init parser parser = argparse.ArgumentParser( description='Convert file.vcxproj to CMakelists.txt') parser.add_argument('-p', help='absolute or relative path of a file.vcxproj') parser.add_argument('-o', help='define output.') parser.add_argument( '-a', help='import cmake code from file.cmake to your final CMakeLists.txt') parser.add_argument( '-D', help='replace dependencies found in .vcxproj, separated by colons.') parser.add_argument('-O', help='define output of artefact produces by CMake.') parser.add_argument( '-i', help='add include directories in CMakeLists.txt. Default : False') # Get args args = parser.parse_args() # Vcxproj Path if args.p is not None: temp_path = os.path.splitext(args.p) if temp_path[1] == '.vcxproj': send('Project to convert = ' + args.p, '') project = Vcxproj() project.create_data(args.p) data['vcxproj'] = project.vcxproj else: send( 'This file is not a ".vcxproj". Be sure you give the right file', 'error') exit(1) # CMakeLists.txt output if args.o is not None: cmakelists = CMake() if os.path.exists(args.o): cmakelists.create_cmake(args.o) data['cmake'] = cmakelists.cmake else: send( 'This path does not exist. CMakeList.txt will be generated in current directory.', 'error') cmakelists.create_cmake() data['cmake'] = cmakelists.cmake else: cmakelists = CMake() cmakelists.create_cmake() data['cmake'] = cmakelists.cmake # CMake additional Code if args.a is not None: data['additional_code'] = args.a # If replace Dependencies if args.D is not None: data['dependencies'] = args.D.split(':') # Define Output of CMake artefact if args.O is not None: data['cmake_output'] = args.O # Add include directories found in vcxproj if args.i is not None: if args.i == 'True': data['includes'] = True # Give all to class: conversata all_data = ConvertData(data) all_data.create_data()