diff --git a/backend.native/build.gradle b/backend.native/build.gradle index 5282e10d4af..297ac48ad09 100644 --- a/backend.native/build.gradle +++ b/backend.native/build.gradle @@ -182,47 +182,58 @@ dependencies { build.dependsOn 'compilerClasses','cli_bcClasses','bc_frontendClasses' + +// These are just a couple of aliases +task stdlib(dependsOn: 'hostStdlib') +task start(dependsOn: 'hostStart') + // These files are built before the 'dist' is complete, // so we provide custom values for // -runtime, -properties, -library and -Djava.library.path -task stdlib(type: JavaExec) { - main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt' - classpath = project.configurations.cli_bc - jvmArgs "-ea", - "-Dkonan.home=${project.parent.file('dist')}", - "-Djava.library.path=${project.buildDir}/nativelibs" - args('-output', project(':runtime').file('build/stdlib.kt.bc'), - '-nolink', '-nostdlib', - '-runtime', project(':runtime').file('build/runtime.bc'), - '-properties', project(':backend.native').file('konan.properties'), +targetList.each { platformName -> + task("${platformName}Stdlib", type: JavaExec) { + main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt' + classpath = project.configurations.cli_bc + jvmArgs "-ea", + "-Dkonan.home=${project.parent.file('dist')}", + "-Djava.library.path=${project.buildDir}/nativelibs" + args('-output', project(':runtime').file("build/${platformName}/stdlib.kt.bc"), + '-nolink', '-nostdlib', + '-target', platformName, + '-runtime', project(':runtime').file("build/${platformName}/runtime.bc"), + '-properties', project(':backend.native').file('konan.properties'), project(':runtime').file('src/main/kotlin'), *project.globalArgs) - inputs.dir(project(':runtime').file('src/main/kotlin')) - outputs.file(project(':runtime').file('build/stdlib.kt.bc')) + inputs.dir(project(':runtime').file('src/main/kotlin')) + outputs.file(project(':runtime').file("build/${platformName}/stdlib.kt.bc")) - dependsOn ':runtime:build' + dependsOn ":runtime:build" + } } -task start(type: JavaExec) { - main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt' - classpath = project.configurations.cli_bc - jvmArgs "-ea", - "-Dkonan.home=${project.parent.file('dist')}", - "-Djava.library.path=${project.buildDir}/nativelibs" - args('-output', project(':runtime').file('build/start.kt.bc'), - '-nolink', '-nostdlib', - '-library', project(':runtime').file('build/stdlib.kt.bc'), - '-runtime', project(':runtime').file('build/runtime.bc'), - '-properties', project(':backend.native').file('konan.properties'), +targetList.each { platformName -> + task("${platformName}Start", type: JavaExec) { + main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt' + classpath = project.configurations.cli_bc + jvmArgs "-ea", + "-Dkonan.home=${project.parent.file('dist')}", + "-Djava.library.path=${project.buildDir}/nativelibs" + args('-output', project(':runtime').file("build/${platformName}/start.kt.bc"), + '-nolink', '-nostdlib', + '-target', platformName, + '-library', project(':runtime').file("build/${platformName}/stdlib.kt.bc"), + '-runtime', project(':runtime').file("build/${platformName}/runtime.bc"), + '-properties', project(':backend.native').file('konan.properties'), project(':runtime').file('src/launcher/kotlin'), *project.globalArgs) - inputs.dir(project(':runtime').file('src/launcher/kotlin')) - outputs.file(project(':runtime').file('build/start.kt.bc')) + inputs.dir(project(':runtime').file('src/launcher/kotlin')) + outputs.file(project(':runtime').file("build/${platformName}/start.kt.bc")) - dependsOn ':runtime:build', 'stdlib' + dependsOn ":runtime:build", "${platformName}Stdlib" + } } task run { diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index 96dffefe77f..0b906b0c73d 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -77,11 +77,13 @@ class K2Native : CLICompiler() { put(EXECUTABLE_FILE, arguments.outputFile ?: "program.kexe") - put(RUNTIME_FILE, - arguments.runtimeFile ?: Distribution.runtime) - put(PROPERTY_FILE, - arguments.propertyFile ?: Distribution.propertyFile) - + if (arguments.runtimeFile != null) + put(RUNTIME_FILE, arguments.runtimeFile) + if (arguments.propertyFile != null) + put(PROPERTY_FILE, arguments.propertyFile) + if (arguments.target != null) + put(TARGET, arguments.target) + put(LIST_TARGETS, arguments.listTargets) put(OPTIMIZATION, arguments.optimization) put(PRINT_IR, arguments.printIr) diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.java b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.java index ac125d346b7..a5904a15c8d 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.java +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.java @@ -30,6 +30,13 @@ public class K2NativeCompilerArguments extends CommonCompilerArguments { @Argument(value = "opt", description = "Enable optimizations during compilation") public boolean optimization; + @Argument(value = "target", description = "Set hardware target") + @ValueDescription("") + public String target; + + @Argument(value = "list_targets", description = "List available hardware targets") + public boolean listTargets; + @Argument(value = "print_ir", description = "Print IR") public boolean printIr; diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Distribution.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Distribution.kt index 789bc0f7109..455eb396c92 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Distribution.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Distribution.kt @@ -1,36 +1,42 @@ package org.jetbrains.kotlin.backend.konan +import org.jetbrains.kotlin.config.CompilerConfiguration import java.io.File import java.util.Properties -public class Distribution(val properties: KonanProperties, - val os: String, val target: String) { +public class Distribution(val config: CompilerConfiguration) { - companion object { + val targetManager = TargetManager(config) + val target = + if (!targetManager.crossCompile) "host" + else targetManager.current.name.toLowerCase() + val suffix = targetManager.currentSuffix() - private fun findKonanHome(): String { - val value = System.getProperty("konan.home", "dist") - val path = File(value).absolutePath - return path - } - - val konanHome = findKonanHome() - val propertyFile = "$konanHome/konan/konan.properties" - - val lib = "$konanHome/lib" - - // TODO: Pack all needed things to dist. - val dependencies = "$konanHome/../dependencies/all" - - val start = "$lib/start.kt.bc" - val stdlib = "$lib/stdlib.kt.bc" - val runtime = "$lib/runtime.bc" - val launcher = "$lib/launcher.bc" + private fun findKonanHome(): String { + val value = System.getProperty("konan.home", "dist") + val path = File(value).absolutePath + return path } - val llvmHome = "$dependencies/${properties.propertyString("llvmHome.$os")}" - val sysRoot = "$dependencies/${properties.propertyString("sysRoot.$os")}" - val libGcc = "$dependencies/${properties.propertyString("libGcc.$os")}" + val konanHome = findKonanHome() + val propertyFile = config.get(KonanConfigKeys.PROPERTY_FILE) + ?: "$konanHome/konan/konan.properties" + val properties = KonanProperties(propertyFile) + + val lib = "$konanHome/lib/$target" + + // TODO: Pack all needed things to dist. + val dependencies = "$konanHome/../dependencies/all" + + val stdlib = "$lib/stdlib.kt.bc" + val start = "$lib/start.kt.bc" + val launcher = "$lib/launcher.bc" + val runtime = config.get(KonanConfigKeys.RUNTIME_FILE) + ?: "$lib/runtime.bc" + + val llvmHome = "$dependencies/${properties.propertyString("llvmHome.$suffix")}" + val sysRoot = "$dependencies/${properties.propertyString("sysRoot.$suffix")}" + val libGcc = "$dependencies/${properties.propertyString("libGcc.$suffix")}" val llvmBin = "$llvmHome/bin" val llvmLib = "$llvmHome/lib" @@ -40,9 +46,9 @@ public class Distribution(val properties: KonanProperties, val llvmLto = "$llvmBin/llvm-lto" val llvmLink = "$llvmBin/llvm-link" val libCppAbi = "$llvmLib/libc++abi.a" - val libLTO = when (os) { - "osx" -> "$llvmLib/libLTO.dylib" - "linux" -> "$llvmLib/libLTO.so" - else -> error("Don't know libLTO location for the platform.") + val libLTO = when (TargetManager.host) { + KonanTarget.MACBOOK -> "$llvmLib/libLTO.dylib" + KonanTarget.LINUX -> "$llvmLib/libLTO.so" + else -> error("Don't know libLTO location for this platform.") } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index faae3d2d54c..53b8fe550ea 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -16,13 +16,15 @@ import java.io.File class KonanConfig(val project: Project, val configuration: CompilerConfiguration) { + internal val distribution = Distribution(configuration) + internal val libraries: List get() { val fromCommandLine = configuration.getList(KonanConfigKeys.LIBRARY_FILES) if (configuration.get(KonanConfigKeys.NOSTDLIB) ?: false) { return fromCommandLine } - return fromCommandLine + Distribution.stdlib + return fromCommandLine + distribution.stdlib } private val loadedDescriptors = loadLibMetadata(libraries) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt index 95ca1afc425..97af9c0eef5 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt @@ -1,60 +1,64 @@ -package org.jetbrains.kotlin.backend.konan; +package org.jetbrains.kotlin.backend.konan -import org.jetbrains.kotlin.config.CompilerConfigurationKey; -import org.jetbrains.kotlin.serialization.js.ModuleKind; +import org.jetbrains.kotlin.config.CompilerConfigurationKey +import org.jetbrains.kotlin.serialization.js.ModuleKind class KonanConfigKeys { companion object { val LIBRARY_FILES: CompilerConfigurationKey> - = CompilerConfigurationKey.create("library file paths"); + = CompilerConfigurationKey.create("library file paths") val BITCODE_FILE: CompilerConfigurationKey - = CompilerConfigurationKey.create("emitted bitcode file path"); + = CompilerConfigurationKey.create("emitted bitcode file path") val EXECUTABLE_FILE: CompilerConfigurationKey - = CompilerConfigurationKey.create("final executable file path"); + = CompilerConfigurationKey.create("final executable file path") val RUNTIME_FILE: CompilerConfigurationKey - = CompilerConfigurationKey.create("override default runtime file path"); + = CompilerConfigurationKey.create("override default runtime file path") val PROPERTY_FILE: CompilerConfigurationKey - = CompilerConfigurationKey.create("override default property file path"); + = CompilerConfigurationKey.create("override default property file path") val ABI_VERSION: CompilerConfigurationKey - = CompilerConfigurationKey.create("current abi version"); + = CompilerConfigurationKey.create("current abi version") val OPTIMIZATION: CompilerConfigurationKey - = CompilerConfigurationKey.create("optimized compilation"); + = CompilerConfigurationKey.create("optimized compilation") val NOSTDLIB: CompilerConfigurationKey - = CompilerConfigurationKey.create("don't link with stdlib"); + = CompilerConfigurationKey.create("don't link with stdlib") val NOLINK: CompilerConfigurationKey - = CompilerConfigurationKey.create("don't link, only produce a bitcode file "); + = CompilerConfigurationKey.create("don't link, only produce a bitcode file ") + val TARGET: CompilerConfigurationKey + = CompilerConfigurationKey.create("target we compile for") + val LIST_TARGETS: CompilerConfigurationKey + = CompilerConfigurationKey.create("list available targets") val SOURCE_MAP: CompilerConfigurationKey> - = CompilerConfigurationKey.create("generate source map"); + = CompilerConfigurationKey.create("generate source map") val META_INFO: CompilerConfigurationKey> - = CompilerConfigurationKey.create("generate metadata"); + = CompilerConfigurationKey.create("generate metadata") val MODULE_KIND: CompilerConfigurationKey - = CompilerConfigurationKey.create("module kind"); + = CompilerConfigurationKey.create("module kind") val VERIFY_IR: CompilerConfigurationKey - = CompilerConfigurationKey.create("verify ir"); + = CompilerConfigurationKey.create("verify ir") val VERIFY_DESCRIPTORS: CompilerConfigurationKey - = CompilerConfigurationKey.create("verify descriptors"); + = CompilerConfigurationKey.create("verify descriptors") val VERIFY_BITCODE: CompilerConfigurationKey - = CompilerConfigurationKey.create("verify bitcode"); + = CompilerConfigurationKey.create("verify bitcode") val PRINT_IR: CompilerConfigurationKey - = CompilerConfigurationKey.create("print ir"); + = CompilerConfigurationKey.create("print ir") val PRINT_DESCRIPTORS: CompilerConfigurationKey - = CompilerConfigurationKey.create("print descriptors"); + = CompilerConfigurationKey.create("print descriptors") val PRINT_BITCODE: CompilerConfigurationKey - = CompilerConfigurationKey.create("print bitcode"); + = CompilerConfigurationKey.create("print bitcode") val ENABLED_PHASES: CompilerConfigurationKey> - = CompilerConfigurationKey.create("enable backend phases"); + = CompilerConfigurationKey.create("enable backend phases") val DISABLED_PHASES: CompilerConfigurationKey> - = CompilerConfigurationKey.create("disable backend phases"); + = CompilerConfigurationKey.create("disable backend phases") val VERBOSE_PHASES: CompilerConfigurationKey> - = CompilerConfigurationKey.create("verbose backend phases"); + = CompilerConfigurationKey.create("verbose backend phases") val LIST_PHASES: CompilerConfigurationKey - = CompilerConfigurationKey.create("list backend phases"); + = CompilerConfigurationKey.create("list backend phases") val TIME_PHASES: CompilerConfigurationKey - = CompilerConfigurationKey.create("time backend phases"); + = CompilerConfigurationKey.create("time backend phases") } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt index ad78da2e2da..af83087103e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.config.kotlinSourceRoots import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration @@ -35,11 +36,18 @@ public fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreEn val config = konanConfig.configuration + val targets = TargetManager(config) + if (config.get(KonanConfigKeys.LIST_TARGETS) ?: false) { + targets.list() + } + KonanPhases.config(konanConfig) if (config.get(KonanConfigKeys.LIST_PHASES) ?: false) { KonanPhases.list() } + if (config.kotlinSourceRoots.isEmpty()) return + val collector = config.getNotNull( CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanProperties.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanProperties.kt index e8770ee8887..4dbc1996d4c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanProperties.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanProperties.kt @@ -1,15 +1,14 @@ package org.jetbrains.kotlin.backend.konan -import org.jetbrains.kotlin.config.CompilerConfiguration import java.io.File import java.util.Properties -public class KonanProperties(config: CompilerConfiguration) { +public class KonanProperties(val propertyFile: String) { val properties = Properties() init { - val file = File(config.get(KonanConfigKeys.PROPERTY_FILE)) + val file = File(propertyFile) file.bufferedReader()?.use { reader -> properties.load(reader) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanTarget.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanTarget.kt new file mode 100644 index 00000000000..fca55c6824d --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanTarget.kt @@ -0,0 +1,104 @@ +package org.jetbrains.kotlin.backend.konan + +import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.config.CompilerConfiguration + +enum class KonanTarget(var enabled: Boolean = false) { + IPHONE(), + IPHONE_SIM(), + LINUX(), + MACBOOK() +} + +class TargetManager(val config: CompilerConfiguration) { + val targets = KonanTarget.values().associate{ it.name.toLowerCase() to it } + val current = determineCurrent() + + init { + when (host) { + KonanTarget.MACBOOK -> KonanTarget.MACBOOK.enabled = true + KonanTarget.LINUX -> KonanTarget.LINUX.enabled = true + } + KonanTarget.IPHONE.enabled = true + KonanTarget.IPHONE_SIM.enabled = true + + if (!current.enabled) { + error("Target $current is not available on the current host") + } + } + + fun known(name: String): String { + if (targets[name] == null) { + error("Unknown target: $name. Use -list_targets to see the list of available targets") + } + return name!! + } + + fun list() { + targets.forEach { key, target -> + if (target.enabled) { + val isDefault = if (target == current) "(default)" else "" + println(String.format("%1$-30s%2$-10s", "$key:", "$isDefault")) + } + } + } + + fun determineCurrent(): KonanTarget { + val userRequest = config.get(KonanConfigKeys.TARGET) + return if (userRequest == null || userRequest == "host") { + host + } else { + targets[known(userRequest)]!! + } + } + + fun currentSuffix(): String { + if (host == KonanTarget.MACBOOK) { + when (current) { + KonanTarget.MACBOOK -> return("osx") + KonanTarget.IPHONE -> return("osx-ios") + KonanTarget.IPHONE_SIM -> return("osx-ios-sim") + else -> error("Impossible combination of $host and $current") + } + } + if (host == KonanTarget.LINUX) { + when (current) { + KonanTarget.LINUX -> return("linux") + KonanTarget.IPHONE -> return("linux-ios") + KonanTarget.IPHONE_SIM -> return("linux-ios-sim") + else -> error("Impossible combination of $host and $current") + } + } + error("Unknown host target $host)") + } + + companion object { + fun host_os(): String { + val javaOsName = System.getProperty("os.name") + return when (javaOsName) { + "Mac OS X" -> "osx" + "Linux" -> "linux" + else -> error("Unknown operating system: ${javaOsName}") + } + } + + fun host_arch(): String { + val javaArch = System.getProperty("os.arch") + return when (javaArch) { + "x86_64" -> "x86_64" + "amd64" -> "x86_64" + "arm64" -> "arm64" + else -> error("Unknown hardware platform: ${javaArch}") + } + } + + val host: KonanTarget = when (host_os()) { + "osx" -> KonanTarget.MACBOOK + "linux" -> KonanTarget.LINUX + else -> error("Unknown host target: ${host_os()} ${host_arch()}") + } + } + + val crossCompile = (host != current) +} + diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt index 5e5a3384db7..eff3e808dd9 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt @@ -10,8 +10,8 @@ typealias ExecutableFile = String // Use "clang -v -save-temps" to write linkCommand() method // for another implementation of this class. -internal abstract class PlatformFlags(val distrib: Distribution, - val properties: KonanProperties) { +internal abstract class PlatformFlags(val distribution: Distribution) { + val properties = distribution.properties abstract val llvmLtoFlags: List abstract val llvmLlcFlags: List @@ -25,8 +25,8 @@ internal abstract class PlatformFlags(val distrib: Distribution, } -internal class MacOSPlatform(distrib: Distribution, - properties: KonanProperties) : PlatformFlags(distrib, properties) { +internal open class MacOSPlatform(distribution: Distribution) + : PlatformFlags(distribution) { override val llvmLtoFlags = properties.propertyList("llvmLtoFlags.osx") override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.osx") @@ -34,28 +34,41 @@ internal class MacOSPlatform(distrib: Distribution, override val linkerOptimizationFlags = properties.propertyList("linkerOptimizationFlags.osx") override val linkerKonanFlags = properties.propertyList("linkerKonanFlags.osx") - override val linker = "${distrib.sysRoot}/usr/bin/ld" + override val linker = "${distribution.sysRoot}/usr/bin/ld" + + open val arch = properties.propertyString("arch.osx")!! + open val osVersionMin = properties.propertyList("osVersionMin.osx") + + open val sysRoot = distribution.sysRoot + open val targetSysRoot = sysRoot + open val llvmLib = distribution.llvmLib override fun linkCommand(objectFiles: List, executable: String, optimize: Boolean): List { - val sysRoot = distrib.sysRoot - val llvmLib = distrib.llvmLib - return mutableListOf(linker, "-demangle") + - if (optimize) listOf("-object_path_lto", "temporary.o", "-lto_library", distrib.libLTO) else {listOf()} + - listOf( "-dynamic", "-arch", "x86_64", "-macosx_version_min") + - properties.propertyList("macosVersionMin.osx") + - listOf("-syslibroot", "$sysRoot", + if (optimize) listOf("-object_path_lto", "temporary.o", "-lto_library", distribution.libLTO) else {listOf()} + + listOf( "-dynamic", "-arch", arch) + + osVersionMin + + listOf("-syslibroot", "$targetSysRoot", "-o", executable) + objectFiles + if (optimize) linkerOptimizationFlags else {listOf()} + linkerKonanFlags + - listOf("-lSystem", "$llvmLib/clang/3.8.0/lib/darwin/libclang_rt.osx.a") + listOf("-lSystem") } } -internal class LinuxPlatform(distrib: Distribution, - properties: KonanProperties) : PlatformFlags(distrib, properties) { +internal class IPhoneOSfromMacOSPlatform(distribution: Distribution) + : MacOSPlatform(distribution) { + + override val arch = properties.propertyString("arch.osx-ios")!! + override val osVersionMin = properties.propertyList("osVersionMin.osx-ios") + override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.osx-ios") + override val targetSysRoot = "${distribution.dependencies}/${properties.propertyString("targetSysRoot.osx-ios")!!}" +} + +internal class LinuxPlatform(distribution: Distribution) + : PlatformFlags(distribution) { override val llvmLtoFlags = properties.propertyList("llvmLtoFlags.linux") override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.linux") @@ -63,16 +76,16 @@ internal class LinuxPlatform(distrib: Distribution, override val linkerOptimizationFlags = properties.propertyList("linkerOptimizationFlags.linux") override val linkerKonanFlags = properties.propertyList("linkerKonanFlags.linux") - override val linker = "${distrib.sysRoot}/../bin/ld.gold" + override val linker = "${distribution.sysRoot}/../bin/ld.gold" val pluginOptimizationFlags = properties.propertyList("pluginOptimizationFlags.linux") override fun linkCommand(objectFiles: List, executable: String, optimize: Boolean): List { - val sysRoot = distrib.sysRoot - val llvmLib = distrib.llvmLib - val libGcc = distrib.libGcc + val sysRoot = distribution.sysRoot + val llvmLib = distribution.llvmLib + val libGcc = distribution.libGcc // TODO: Can we extract more to the konan.properties? return mutableListOf("$linker", @@ -97,31 +110,27 @@ internal class LinuxPlatform(distrib: Distribution, internal class LinkStage(val context: Context) { - - private val javaOsName = System.getProperty("os.name") - private val javaArch = System.getProperty("os.arch") - - val os = when (javaOsName) { - "Mac OS X" -> "osx" - "Linux" -> "linux" - else -> error("Unknown operating system: ${javaOsName}") - } - val arch = when (javaArch) { - "x86_64" -> "x86_64" - "amd64" -> "x86_64" - else -> error("Unknown hardware platform: ${javaArch}") - } - - private val properties = KonanProperties(context.config.configuration) - private val distrib = Distribution(properties, os, arch) - - val platform: PlatformFlags = when (os) { - "linux" -> LinuxPlatform(distrib, properties) - "osx" -> MacOSPlatform(distrib, properties) - else -> error("Could not tell the current platform") - } val config = context.config.configuration + + val targetManager = TargetManager(config) + private val distribution = + Distribution(context.config.configuration) + private val properties = distribution.properties + + val platform: PlatformFlags = when (TargetManager.host) { + KonanTarget.LINUX -> LinuxPlatform(distribution) + KonanTarget.MACBOOK -> when (targetManager.current) { + KonanTarget.IPHONE + -> IPhoneOSfromMacOSPlatform(distribution) + KonanTarget.MACBOOK + -> MacOSPlatform(distribution) + else -> TODO("Target not implemented yet.") + } + else -> TODO("Target not implemented yet") + } + val suffix = targetManager.currentSuffix() + val optimize = config.get(KonanConfigKeys.OPTIMIZATION) ?: false val emitted = config.get(KonanConfigKeys.BITCODE_FILE)!! val nostdlib = config.get(KonanConfigKeys.NOSTDLIB) ?: false @@ -131,7 +140,7 @@ internal class LinkStage(val context: Context) { // TODO: Make it a temporary file. val combined = "combined.o" - val tool = distrib.llvmLto + val tool = distribution.llvmLto val command = mutableListOf(tool, "-o", combined) if (optimize) { command.addAll(platform.llvmLtoFlags) @@ -146,9 +155,9 @@ internal class LinkStage(val context: Context) { // TODO: Make it a temporary file. val objectFile = "$file.o" - val tool = distrib.llvmLlc - val command = listOf(distrib.llvmLlc, "-o", objectFile, "-filetype=obj") + - properties.propertyList("llvmLlcFlags.$os") + listOf(file) + val tool = distribution.llvmLlc + val command = listOf(distribution.llvmLlc, "-o", objectFile, "-filetype=obj") + + properties.propertyList("llvmLlcFlags.$suffix") + listOf(file) runTool(*command.toTypedArray()) return objectFile @@ -186,10 +195,10 @@ internal class LinkStage(val context: Context) { } fun linkStage() { - context.log("# Compiler root: ${Distribution.konanHome}") + context.log("# Compiler root: ${distribution.konanHome}") - val bitcodeFiles = listOf(emitted, Distribution.start, Distribution.runtime, - Distribution.launcher) + libraries + val bitcodeFiles = listOf(emitted, distribution.start, distribution.runtime, + distribution.launcher) + libraries val objectFiles = if (optimize) { listOf( llvmLto(bitcodeFiles ) ) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 1724d7e1dc6..877e0ef6f0e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -250,7 +250,7 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) { val staticData = StaticData(context) - val runtimeFile = context.config.configuration.get(KonanConfigKeys.RUNTIME_FILE)!! + val runtimeFile = context.config.distribution.runtime val runtime = Runtime(runtimeFile) // TODO: dispose init { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 757a49345f3..8088e1d225a 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -33,6 +33,11 @@ import org.jetbrains.kotlin.utils.addToStdlib.singletonList internal fun emitLLVM(context: Context) { val irModule = context.irModule!! + // Note that we don't set module target explicitly. + // It is determined by the target of runtime.bc + // (see Llvm class in ContextUtils) + // Which in turn is determined by the clang flags + // used to compile runtime.bc. val llvmModule = LLVMModuleCreateWithName("out")!! // TODO: dispose context.llvmModule = llvmModule diff --git a/backend.native/konan.properties b/backend.native/konan.properties index c45907461ec..729f32031dc 100644 --- a/backend.native/konan.properties +++ b/backend.native/konan.properties @@ -1,19 +1,28 @@ // TODO: utilize substitution mechanism from interop. +arch.osx = x86_64 sysRoot.osx = target-sysroot-1-darwin-macos llvmHome.osx = clang+llvm-3.8.0-darwin-macos - -sysRoot.linux = target-gcc-toolchain-3-linux-x86-64/x86_64-unknown-linux-gnu/sysroot -llvmHome.linux = clang+llvm-3.8.0-linux-x86-64 -libGcc.linux = target-gcc-toolchain-3-linux-x86-64/lib/gcc/x86_64-unknown-linux-gnu/4.8.5 - llvmLtoFlags.osx = -O3 -function-sections -exported-symbol=_main llvmLlcFlags.osx = -mtriple=x86_64-apple-macosx10.10.0 linkerKonanFlags.osx = -lc++ linkerOptimizationFlags.osx = -dead_strip -macosVersionMin.osx = 10.10.0 +osVersionMin.osx = -macosx_version_min 10.10.0 +arch.osx-ios = arm64 +sysRoot.osx-ios = target-sysroot-1-darwin-macos +targetSysRoot.osx-ios = target-sysroot-1-darwin-ios +llvmHome.osx-ios = clang+llvm-3.8.0-darwin-macos +llvmLtoFlags.osx-ios = -O3 -function-sections -exported-symbol=_main +llvmLlcFlags.osx-ios = -mtriple=arm64-apple-ios5.0.0 +linkerKonanFlags.osx-ios = -lc++ +linkerOptimizationFlags.osx-ios = -dead_strip +osVersionMin.osx-ios = -iphoneos_version_min 5.0.0 + +sysRoot.linux = target-gcc-toolchain-3-linux-x86-64/x86_64-unknown-linux-gnu/sysroot +llvmHome.linux = clang+llvm-3.8.0-linux-x86-64 +libGcc.linux = target-gcc-toolchain-3-linux-x86-64/lib/gcc/x86_64-unknown-linux-gnu/4.8.5 llvmLtoFlags.linux = -O3 -function-sections -exported-symbol=main llvmLlcFlags.linux = -march=x86-64 linkerKonanFlags.linux = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread diff --git a/build.gradle b/build.gradle index 71dbcea2d6b..322a36a912e 100644 --- a/build.gradle +++ b/build.gradle @@ -22,28 +22,43 @@ allprojects { } } - - convention.plugins.clangFlags = new ClangFlags(new PlatformInfo(), "$llvmDir") ext.clangPath = ["$llvmDir/bin"] ext.clangArgs = [] + ext.targetArgs = [:] final String binDir + + if (isLinux()) { + ext.targetArgs << + ["host": ["--sysroot=${gccToolchainDir}/${gnuTriplet}/sysroot"]] + } else { + ext.targetArgs << + ["host": + ["--sysroot=$hostSysrootDir"], + "iphone": + ["-stdlib=libc++", "-arch", "arm64", "-isysroot", "$iphoneSysrootDir"], + "iphone_sim": + ["-stdlib=libc++"] + ] + } + ext.targetList = ext.targetArgs.keySet() as List + if (isLinux()) { binDir = "$gccToolchainDir/$gnuTriplet/bin" ext.clangArgs.addAll([ - "--sysroot=$gccToolchainDir/$gnuTriplet/sysroot", "--gcc-toolchain=$gccToolchainDir", "-L$llvmDir/lib" ]) } else { - binDir = "$sysrootDir/usr/bin" - ext.clangArgs << "--sysroot=$sysrootDir" + binDir = "$hostSysrootDir/usr/bin" } ext.clangArgs << "-B$binDir" + ext.hostClangArgs = ext.clangArgs.clone() + ext.hostClangArgs.addAll(ext.targetArgs['host']) + ext.clangPath << binDir ext.jvmArgs = ["-ea"] - ext.clangLinkArgs = libLink() - ext.clangOptArgs = optArgs() ext.globalArgs = project.hasProperty("konanc_flags") ? ext.konanc_flags.split(' ') : [] + convention.plugins.execClang = new org.jetbrains.kotlin.ExecClang(project) plugins.withType(NativeComponentPlugin) { @@ -56,7 +71,7 @@ allprojects { eachPlatform { // TODO: will not work when cross-compiling [cCompiler, cppCompiler, linker].each { - it.withArguments { it.addAll(clangArgs) } + it.withArguments { it.addAll(hostClangArgs) } } } @@ -66,56 +81,6 @@ allprojects { } } -class ClangFlags { - - protected PlatformInfo platform - protected String llvmDir - - private List linkCppAbi, linkPlatform, optArgs - - ClangFlags(platformInfo, llvm) { - platform = platformInfo - llvmDir = llvm - - if (platform.isLinux()) { - linkCppAbi= ["-Wl,-Bstatic", "-lc++abi", "-Wl,-Bdynamic"] - linkPlatform= ["-ldl", "-lpthread", "-lm", "-rdynamic"] - optArgs = ["-O3", "-fuse-ld=gold", "-flto", "-ffunction-sections", "-Wl,--gc-sections"] - } else if (platform.isMac()) { - // Life is hard. MacOS linker is harder. - // This is how we force the linker to link statically. - linkCppAbi= ["${llvmDir}/lib/libc++abi.a"] - linkPlatform= [] - optArgs = ["-O3", "-flto", "-Wl,-dead_strip"] - } else { - throw unsupportedPlatformException() - } - - } - - public List linkCppAbi() { - return linkCppAbi - } - - public List linkPlatform() { - return linkPlatform - } - - public List optArgs() { - return optArgs - } - - - public List libLink() { - def libs = [] - libs.addAll(linkPlatform()) - libs.addAll(linkCppAbi()) - return libs - } -} - - - class PlatformInfo { private final String osName = System.properties['os.name'] private final String osArch = System.properties['os.arch'] @@ -170,13 +135,6 @@ task dist_compiler(type: Copy) { fileMode(0755) include('konanc') include('kotlinc-native') - - def allClangArgs = "${clangArgs.join(' ')} ${clangLinkArgs.join(' ')}" - - filter { line -> line.replaceAll("FILTER_CLANG_PLATFORM_ARGS", allClangArgs) } - filter { line -> line.replaceAll("FILTER_CLANG_PLATFORM_OPT", "${clangOptArgs.join(' ')}") } - filter { line -> line.replaceAll("FILTER_CLANG_BIN_PATH", "${project.llvmDir}/bin") } - into('bin') } @@ -190,12 +148,27 @@ task list_dist(type: Exec) { } task dist_runtime(type: Copy) { - dependsOn ':runtime:build', ':backend.native:stdlib', ':backend.native:start' + dependsOn ':runtime:build' + dependsOn ':backend.native:hostStdlib' + dependsOn ':backend.native:hostStart' destinationDir file('dist') from(project(':runtime').file('build')) { - include('*.bc') + include('*/*.bc') + into('lib') + } +} + +task cross_dist_runtime(type: Copy) { + dependsOn ':runtime:build' + dependsOn.addAll(targetList.collect { ":backend.native:${it}Stdlib" }) + dependsOn.addAll(targetList.collect { ":backend.native:${it}Start" }) + + destinationDir file('dist') + + from(project(':runtime').file('build')) { + include('*/*.bc') into('lib') } } @@ -204,6 +177,10 @@ task dist { dependsOn 'dist_compiler', 'dist_runtime' } +task cross_dist { + dependsOn 'cross_dist_runtime', 'dist_compiler' +} + task demo(type: Exec) { dependsOn 'dist' diff --git a/buildSrc/src/main/groovy/org/jetbrains/kotlin/CompileToBitcode.groovy b/buildSrc/src/main/groovy/org/jetbrains/kotlin/CompileToBitcode.groovy index da6ab4b27a9..e60343b83f5 100644 --- a/buildSrc/src/main/groovy/org/jetbrains/kotlin/CompileToBitcode.groovy +++ b/buildSrc/src/main/groovy/org/jetbrains/kotlin/CompileToBitcode.groovy @@ -1,16 +1,65 @@ package org.jetbrains.kotlin import org.gradle.api.DefaultTask +import org.gradle.api.tasks.* import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction +class CompilePerTarget extends CompileCppToBitcode { + private List targetList = [] + private Map> targetArgs = null + private Map> targetLinkerArgs = null + + void targetList(List list) { + targetList.addAll(list) + } + + void targetArgs(Map> map) { + targetArgs = map + } + + private Map> getTargetArgs() { + return targetArgs + } + + void targetLinkerArgs(Map> map) { + targetLinkerArgs = map + } + + private Map> getTargetLinkerArgs() { + return targetLinkerArgs + } + + @TaskAction + void compile() { + def targetList = this.targetList + def targetArgs = getTargetArgs() + def targetLinkerArgs = getTargetLinkerArgs() + def commonCompilerArgs = getCompilerArgs().clone() + def commonLinkerArgs = getLinkerArgs().clone() + targetList.each { + this.compilerArgs = [] + this.linkerArgs = [] + target(it) + compilerArgs(commonCompilerArgs) + if (targetArgs != null) + compilerArgs(targetArgs[it]) + linkerArgs(commonLinkerArgs) + if (targetLinkerArgs != null) + linkerArgs(targetLinkerArgs[it]) + super.compile() + } + } +} + class CompileCppToBitcode extends DefaultTask { private String name = "main" + private String target = "host" private File srcRoot; - private List compilerArgs = [] - private List linkerArgs = [] + protected List compilerArgs = [] + protected List linkerArgs = [] @InputDirectory File getSrcRoot() { @@ -19,7 +68,7 @@ class CompileCppToBitcode extends DefaultTask { @OutputFile File getOutFile() { - return new File(project.buildDir, "${name}.bc") + return new File(getTargetDir(), "${name}.bc") } private File getSrcDir() { @@ -30,23 +79,31 @@ class CompileCppToBitcode extends DefaultTask { return new File(this.getSrcRoot(), "headers") } + private File getTargetDir() { + return new File(project.buildDir, target) + } + private File getObjDir() { - return new File(project.buildDir, name) + return new File(getTargetDir(), name) } void name(String value) { name = value } + void target(String value) { + target = value + } + void srcRoot(File value) { srcRoot = value } - private List getCompilerArgs() { + protected List getCompilerArgs() { return compilerArgs } - private List getLinkerArgs() { + protected List getLinkerArgs() { return linkerArgs } @@ -54,10 +111,18 @@ class CompileCppToBitcode extends DefaultTask { compilerArgs.addAll(args) } + void compilerArgs(List args) { + compilerArgs.addAll(args) + } + void linkerArgs(String... args) { linkerArgs.addAll(args) } + void linkerArgs(List args) { + linkerArgs.addAll(args) + } + @TaskAction void compile() { // the strange code below seems to be required due to some Gradle (Groovy?) behaviour diff --git a/buildSrc/src/main/groovy/org/jetbrains/kotlin/ExecClang.groovy b/buildSrc/src/main/groovy/org/jetbrains/kotlin/ExecClang.groovy index b4e87ef4b29..2576259b0b6 100644 --- a/buildSrc/src/main/groovy/org/jetbrains/kotlin/ExecClang.groovy +++ b/buildSrc/src/main/groovy/org/jetbrains/kotlin/ExecClang.groovy @@ -34,7 +34,7 @@ class ExecClang { if (executable in ['clang', 'clang++']) { executable = "${project.llvmDir}/bin/$executable" } else { - throw new GradleException("unsupport clang executable: $executable") + throw new GradleException("unsupported clang executable: $executable") } environment["PATH"] = project.files(project.clangPath).asPath + @@ -47,4 +47,4 @@ class ExecClang { } return project.exec(extendedAction) } -} \ No newline at end of file +} diff --git a/buildSrc/src/main/groovy/org/jetbrains/kotlin/KonanTest.groovy b/buildSrc/src/main/groovy/org/jetbrains/kotlin/KonanTest.groovy index 111dfc1ae2e..930c92b75ca 100644 --- a/buildSrc/src/main/groovy/org/jetbrains/kotlin/KonanTest.groovy +++ b/buildSrc/src/main/groovy/org/jetbrains/kotlin/KonanTest.groovy @@ -12,7 +12,6 @@ abstract class KonanTest extends DefaultTask { def backendNative = project.project(":backend.native") def runtimeProject = project.project(":runtime") def dist = project.rootProject.file("dist") - def llvmLlc = llvmTool("llc") def runtimeBc = new File("${dist.canonicalPath}/lib/runtime.bc").absolutePath def launcherBc = new File("${dist.canonicalPath}/lib/launcher.bc").absolutePath def startKtBc = new File("${dist.canonicalPath}/lib/start.kt.bc").absolutePath @@ -100,14 +99,6 @@ abstract class KonanTest extends DefaultTask { if (goldValue != null && goldValue != out.toString()) throw new RuntimeException("test failed.") } - - private String llvmTool(String tool) { - return "${project.llvmDir}/bin/${tool}" - } - - protected List clangLinkArgs() { - return project.clangLinkArgs - } } class RunKonanTest extends KonanTest { diff --git a/buildSrc/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy b/buildSrc/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy index 5dd8a6acdfe..8a0d718cc9d 100644 --- a/buildSrc/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy +++ b/buildSrc/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy @@ -182,8 +182,8 @@ class NamedNativeInteropConfig implements Named { environment['PATH'] = project.files(project.clangPath).asPath + File.pathSeparator + environment['PATH'] - compilerOpts += project.clangArgs - linkerOpts += project.clangArgs + compilerOpts += project.hostClangArgs + linkerOpts += project.hostClangArgs args compilerOpts.collect { "-copt:$it" } args linkerOpts.collect { "-lopt:$it" } diff --git a/common/build.gradle b/common/build.gradle index 09d78a6f1c1..a676dfa0c6a 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -1,10 +1,11 @@ -import org.jetbrains.kotlin.CompileCppToBitcode +import org.jetbrains.kotlin.CompilePerTarget // TODO: consider using some Gradle plugins to build and test - -task compileHash(type: CompileCppToBitcode) { +task compileHash(type: CompilePerTarget) { name 'hash' + targetList targetList + targetArgs targetArgs } task build { diff --git a/dependencies/build.gradle b/dependencies/build.gradle index 2cd09a4120e..0faa97197e4 100644 --- a/dependencies/build.gradle +++ b/dependencies/build.gradle @@ -10,7 +10,7 @@ abstract class NativeDep extends DefaultTask { } } - protected final String target = getCurrentHostTarget(); + protected final String host = getCurrentHostTarget(); @Input abstract String getFileName() @@ -27,7 +27,7 @@ abstract class NativeDep extends DefaultTask { protected File download() { File result = new File(baseOutDir, fileName) - if (!result.exists()) + if (!result.exists()) ant.get(src: url, dest: result, usetimestamp: true) return result } @@ -71,20 +71,23 @@ class TgzNativeDep extends NativeDep { task libffi(type: TgzNativeDep) { - baseName = "libffi-3.2.1-2-$target" + baseName = "libffi-3.2.1-2-$host" } task llvm(type: TgzNativeDep) { - baseName = "clang+llvm-3.8.0-$target" + baseName = "clang+llvm-3.8.0-$host" } if (isLinux()) { task gccToolchain(type: TgzNativeDep) { - baseName = "target-gcc-toolchain-3-$target" + baseName = "target-gcc-toolchain-3-$host" } } else { - task sysroot(type: TgzNativeDep) { - baseName = "target-sysroot-1-$target" + task hostSysroot(type: TgzNativeDep) { + baseName = "target-sysroot-1-$host" + } + task iphoneSysroot(type: TgzNativeDep) { + baseName = "target-sysroot-1-darwin-ios" } } @@ -95,4 +98,4 @@ task update { tasks.withType(TgzNativeDep) { rootProject.ext.set("${name}Dir", outputDir.path) -} \ No newline at end of file +} diff --git a/runtime/build.gradle b/runtime/build.gradle index b0f42ba7b57..11baaf62517 100644 --- a/runtime/build.gradle +++ b/runtime/build.gradle @@ -1,21 +1,26 @@ -import org.jetbrains.kotlin.CompileCppToBitcode +import org.jetbrains.kotlin.CompilePerTarget // TODO: consider using some Gradle plugins to build and test -task build(type: CompileCppToBitcode) { +task build(type: CompilePerTarget) { name 'runtime' srcRoot file('src/main') - - dependsOn ':common:compileHash' - dependsOn 'launcher' + dependsOn ":common:compileHash" + dependsOn "launcher" + targetList targetList + targetArgs targetArgs compilerArgs '-I' + project.file('../common/src/hash/headers') // TODO: copy-pasted from 'common' - linkerArgs project.file('../common/build/hash.bc').path + targetLinkerArgs targetList.collectEntries { + [it,[project.file("../common/build/$it/hash.bc")]] + } } -task launcher(type: CompileCppToBitcode) { +task launcher(type: CompilePerTarget) { name 'launcher' srcRoot file('src/launcher') + targetList targetList + targetArgs targetArgs compilerArgs '-I' + project.file('../common/src/hash/headers') // TODO: copy-pasted from 'common' compilerArgs '-I' + project.file('src/main/cpp') }