From 334d2f0ee6cf9cde6bf212b36f29c8118b61d355 Mon Sep 17 00:00:00 2001 From: Alexander Gorshenev Date: Wed, 22 Mar 2017 17:57:40 +0300 Subject: [PATCH] Remote test execution for cross compiled tests. Run it like: $ ./gradlew backend.native:tests:run \ -Premote=user@111.22.33.444 -Ptest_target=raspberrypi The new gradle command line options: -Pbuild_flags renamed -Pkonanc_flags. -Ptest_flags provides compiler flags to the test builds. -Ptest_target properly sets up compiler as well as interop to cross compile tests. -Premote specifies remote host and login. --- HACKING.md | 19 ++++- backend.native/build.gradle | 4 +- backend.native/konan.properties | 5 +- backend.native/tests/build.gradle | 8 ++ backend.native/tests/interop/basics/3.kt | 2 +- build.gradle | 75 ++++++++++++------- .../org/jetbrains/kotlin/ExecRemote.groovy | 69 +++++++++++++++++ .../org/jetbrains/kotlin/KonanTest.groovy | 8 +- .../kotlin/NativeInteropPlugin.groovy | 13 +++- gradle.properties | 1 + samples/tetris/build.sh | 4 + 11 files changed, 171 insertions(+), 37 deletions(-) create mode 100644 buildSrc/src/main/groovy/org/jetbrains/kotlin/ExecRemote.groovy diff --git a/HACKING.md b/HACKING.md index 20b7de4ceec..2f9330f35b9 100644 --- a/HACKING.md +++ b/HACKING.md @@ -2,9 +2,9 @@ There are several gradle flags one can use for Konan build. -* **-Pkonanc_flags** passes flags to the compiler used to build stdlib +* **-Pbuild_flags** passes flags to the compiler used to build stdlib - ./gradlew -Pkonanc_flags="--disable lower_inline --print_ir" stdlib + ./gradlew -Pbuild_flags="--disable lower_inline --print_ir" stdlib * **-Pshims** compiles LLVM interface with tracing "shims". Allowing one to trace the LLVM calls from the compiler. @@ -31,4 +31,17 @@ To update the blackbox compiler tests set TeamCity build number in `gradle.prope testDataVersion=:id -and run `./gradlew update_external_tests` \ No newline at end of file +and run `./gradlew update_external_tests` + +* **-Ptest_flags** passes flags to the compiler used to compile tests + + ./gradlew -Ptest_flags="--time" backend.native:tests:array0 + +* **-Ptest_target** specifies cross target for a test run. + + ./gradlew -Ptest_target=raspberrypi backend.native:tests:array0 + +* **-Premote=user@host** sets remote test execution login/hostname. Good for cross compiled tests. + + ./gradles -Premote=kotlin@111.22.33.444 backend.native:tests:run + diff --git a/backend.native/build.gradle b/backend.native/build.gradle index 2a987e4c557..e2d3417bcec 100644 --- a/backend.native/build.gradle +++ b/backend.native/build.gradle @@ -204,7 +204,7 @@ targetList.each { target -> project(':runtime').file('src/main/kotlin'), project(':Interop:Runtime').file('src/main/kotlin'), project(':Interop:Runtime').file('src/native/kotlin'), - *project.globalArgs) + *project.globalBuildArgs) inputs.dir(project(':runtime').file('src/main/kotlin')) inputs.dir(project(':Interop:Runtime').file('src/main/kotlin')) @@ -230,7 +230,7 @@ targetList.each { target -> '-runtime', project(':runtime').file("build/${target}/runtime.bc"), '-properties', project(':backend.native').file('konan.properties'), project(':runtime').file('src/launcher/kotlin'), - *project.globalArgs) + *project.globalBuildArgs) inputs.dir(project(':runtime').file('src/launcher/kotlin')) outputs.file(project(':runtime').file("build/${target}/start.kt.bc")) diff --git a/backend.native/konan.properties b/backend.native/konan.properties index 0355fef6eb5..3bfc4d00e44 100644 --- a/backend.native/konan.properties +++ b/backend.native/konan.properties @@ -65,8 +65,11 @@ targetSysRoot.linux-raspberrypi = target-sysroot-1-raspberrypi llvmHome.linux-raspberrypi = clang+llvm-3.9.0-linux-x86-64 libGcc.linux-raspberrypi = target-sysroot-1-raspberrypi/lib/gcc/arm-linux-gnueabihf/4.8.3/ gccToolChain.linux-raspberrypi = target-gcc-toolchain-3-linux-x86-64 -llvmLlcFlags.linux-raspberrypi = -mtriple=armv7-unknown-linux-gnueabihf --disable-fp-elim -mfloat-abi=hard +llvmLtoFlags.linux-raspberrypi = -O3 -function-sections -exported-symbol=Konan_main +llvmLlcFlags.linux-raspberrypi = -mtriple=armv7-unknown-linux-gnueabihf --disable-fp-elim -float-abi=hard linkerKonanFlags.linux-raspberrypi = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lc -lpthread -fuse-ld=gold +entrySelector.linux-raspberrypi = --defsym main=Konan_main dependencies.raspberrypi = clang+llvm-3.9.0-linux-x86-64 \ target-gcc-toolchain-3-linux-x86-64 \ target-sysroot-1-raspberrypi + diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 6048e4d9cc0..bf42fe36815 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -29,6 +29,8 @@ ext.testOutputRoot = rootProject.file("test.output").absolutePath ext.externalTestsDir = project.file("external") externalTestsDir.mkdirs() +project.convention.plugins.remoteExec = new org.jetbrains.kotlin.ExecRemote(project) + allprojects { // Root directories for test output (logs, compiled files, statistics etc). Only single path must be in each set. sourceSets { @@ -1604,6 +1606,12 @@ task interop2(type: RunInteropKonanTest) { } task interop3(type: RunInteropKonanTest) { + // We disable this test on Raspberry Pi because + // qsort accepts size_t args. So the .kt file + // should be different depending on the bitness of the target. + // There are plans to provide a solution and turn this + // test back on. + disabled = (project.testTarget == 'raspberrypi') goldValue = "8 9 12 13 14 \n" source = "interop/basics/3.kt" interop = 'cstdlib' diff --git a/backend.native/tests/interop/basics/3.kt b/backend.native/tests/interop/basics/3.kt index 9840c8ad9bb..9b14c844802 100644 --- a/backend.native/tests/interop/basics/3.kt +++ b/backend.native/tests/interop/basics/3.kt @@ -26,4 +26,4 @@ private fun comparator(a: COpaquePointer?, b: COpaquePointer?): Int { val bValue = b!!.reinterpret().pointed.value return (aValue - bValue) -} \ No newline at end of file +} diff --git a/build.gradle b/build.gradle index abcdb36b937..56cf1cefb40 100644 --- a/build.gradle +++ b/build.gradle @@ -2,12 +2,6 @@ defaultTasks 'clean', 'demo' -if (new File("$project.rootDir/local.properties").exists()) { - Properties props = new Properties() - props.load(new FileInputStream("$project.rootDir/local.properties")) - props.each {prop -> project.ext.set(prop.key, prop.value)} -} - convention.plugins.platformInfo = new PlatformInfo() allprojects { @@ -19,6 +13,37 @@ allprojects { mavenCentral() } + loadCommandLineProperties() + loadLocalProperties() + setupCompilationFlags() + setupClang() +} + +void setupClang() { + + convention.plugins.execClang = new org.jetbrains.kotlin.ExecClang(project) + + plugins.withType(NativeComponentPlugin) { + model { + toolChains { + clang(Clang) { + clangPath.each { + path it + } + + eachPlatform { // TODO: will not work when cross-compiling + [cCompiler, cppCompiler, linker].each { + it.withArguments { it.addAll(hostClangArgs) } + } + + } + } + } + } + } +} + +void setupCompilationFlags() { ext.clangPath = ["$llvmDir/bin"] ext.clangArgs = [] ext.targetArgs = [:] @@ -64,30 +89,28 @@ allprojects { ext.clangPath << binDir ext.jvmArgs = ["-ea"] - ext.globalArgs = project.hasProperty("konanc_flags") ? ext.konanc_flags.split(' ') : [] +} - convention.plugins.execClang = new org.jetbrains.kotlin.ExecClang(project) - - plugins.withType(NativeComponentPlugin) { - model { - toolChains { - clang(Clang) { - clangPath.each { - path it - } - - eachPlatform { // TODO: will not work when cross-compiling - [cCompiler, cppCompiler, linker].each { - it.withArguments { it.addAll(hostClangArgs) } - } - - } - } - } - } +void loadLocalProperties() { + if (new File("$project.rootDir/local.properties").exists()) { + Properties props = new Properties() + props.load(new FileInputStream("$project.rootDir/local.properties")) + props.each {prop -> project.ext.set(prop.key, prop.value)} } } +void loadCommandLineProperties() { + if (project.hasProperty("konanc_flags")) { + throw new Error("Specify either -Ptest_flags or -Pbuild_flags.") + } + ext.globalBuildArgs = project.hasProperty("build_flags") + ? ext.build_flags.split(' ') : [] + ext.globalTestArgs = project.hasProperty("test_flags") + ? ext.test_flags.split(' ') : [] + ext.testTarget = project.hasProperty("test_target") + ? ext.test_target : null +} + class PlatformInfo { private final String osName = System.properties['os.name'] private final String osArch = System.properties['os.arch'] diff --git a/buildSrc/src/main/groovy/org/jetbrains/kotlin/ExecRemote.groovy b/buildSrc/src/main/groovy/org/jetbrains/kotlin/ExecRemote.groovy new file mode 100644 index 00000000000..9bbace0c64f --- /dev/null +++ b/buildSrc/src/main/groovy/org/jetbrains/kotlin/ExecRemote.groovy @@ -0,0 +1,69 @@ +package org.jetbrains.kotlin + +import org.gradle.api.Action +import org.gradle.api.GradleException +import org.gradle.api.Project +import org.gradle.process.ExecResult +import org.gradle.process.ExecSpec +import org.gradle.util.ConfigureUtil + +// This class provides process.execRemote -- a drop-in replacement for process.exec . +// If we provide -Premote=user@host the binaries are executed on a remote host +// If we omit -Premote then the binary is executed locally as usual. + +class ExecRemote { + + private final Project project + private final String remote = null + private final String remoteDir = null + + ExecRemote(Project project) { + this.project = project + if (project.hasProperty('remote')) { + remote = project.ext.remote + remoteDir = uniqSessionName() + createRemoteDir() + } + } + + private String uniqSessionName() { + def date = new Date().format('yyyyMMddHHmmss') + return project.ext.remoteRoot + File.createTempFile(System.properties['user.name'], "_" + date) + } + + private createRemoteDir() { + project.exec { + commandLine('ssh', remote, 'mkdir', '-p', remoteDir) + } + } + + private upload(String fileName) { + project.exec { + commandLine('scp', fileName, "${remote}:${remoteDir}") + } + } + + public ExecResult execRemote(Action action) { + Action extendedAction = new Action() { + @Override + void execute(ExecSpec execSpec) { + action.execute(execSpec) + + if (remote == null) return + + execSpec.with { + upload(getExecutable()) + executable = "$remoteDir/${new File(executable).name}" + if (project.hasProperty('remote')) { + commandLine = ['/usr/bin/ssh', remote] + commandLine + } + } + } + } + return project.exec(extendedAction) + } + + public ExecResult execRemote(Closure closure) { + return this.execRemote(ConfigureUtil.configureUsing(closure)); + } +} diff --git a/buildSrc/src/main/groovy/org/jetbrains/kotlin/KonanTest.groovy b/buildSrc/src/main/groovy/org/jetbrains/kotlin/KonanTest.groovy index 5d9377b4201..b3ae72f0108 100644 --- a/buildSrc/src/main/groovy/org/jetbrains/kotlin/KonanTest.groovy +++ b/buildSrc/src/main/groovy/org/jetbrains/kotlin/KonanTest.groovy @@ -82,7 +82,10 @@ abstract class KonanTest extends JavaExec { "-ea", *filesToCompile, *moreArgs, - *project.globalArgs] + *project.globalTestArgs] + if (project.testTarget) { + args "-target", project.testTarget + } standardOutput = log errorOutput = log super.exec() @@ -162,7 +165,7 @@ abstract class KonanTest extends JavaExec { def out = null //TODO Add test timeout - ExecResult execResult = project.exec { + ExecResult execResult = project.execRemote { commandLine exe if (arguments != null) { args arguments @@ -210,6 +213,7 @@ class RunInteropKonanTest extends KonanTest { void setInterop(String value) { this.interop = value this.interopConf = project.kotlinNativeInterop[value] + this.interopConf.target = project.testTarget this.dependsOn(this.interopConf.genTask) } diff --git a/buildSrc/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy b/buildSrc/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy index 923ef421a1a..09f21026371 100644 --- a/buildSrc/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy +++ b/buildSrc/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy @@ -27,7 +27,8 @@ class NamedNativeInteropConfig implements Named { private String defFile - private String pkg; + private String pkg + private String target private List compilerOpts = [] private List headers; @@ -47,6 +48,10 @@ class NamedNativeInteropConfig implements Named { genTask.inputs.file(project.file(defFile)) } + void target(String value) { + target = value + } + void pkg(String value) { pkg = value } @@ -191,7 +196,7 @@ class NamedNativeInteropConfig implements Named { args "-natives:" + nativeLibsDir args "-flavor:" + this.flavor // Uncomment to debug. - //args "-verbose:true" + // args "-verbose:true" if (defFile != null) { args "-def:" + project.file(defFile) @@ -205,6 +210,10 @@ class NamedNativeInteropConfig implements Named { args "-linker:" + linker } + if (target != null) { + args "-target:" + target + } + // TODO: the interop plugin should probably be reworked to execute clang from build scripts directly environment['PATH'] = project.files(project.clangPath).asPath + File.pathSeparator + environment['PATH'] diff --git a/gradle.properties b/gradle.properties index bcd0abec3d5..00427b8eba0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,7 @@ kotlin_version=1.1.0 llvmVersion = 3.9.0 minMacOsVersion = 10.10 +remoteRoot=konan_tests #kotlinCompilerModule=org.jetbrains.kotlin:kotlin-compiler:1.1-SNAPSHOT # Download artifacts of https://teamcity.jetbrains.com/viewType.html?buildTypeId=bt345 testDataVersion=1009012:id diff --git a/samples/tetris/build.sh b/samples/tetris/build.sh index 77430862c18..be26c718ee7 100755 --- a/samples/tetris/build.sh +++ b/samples/tetris/build.sh @@ -9,16 +9,20 @@ LINKER_ARGS_macbook="-F /Library/Frameworks -framework SDL2" COMPILER_ARGS_macbook= CFLAGS_macbook=-I/opt/local/include/SDL2 LINKER_ARGS_macbook="-L/opt/local/lib -lSDL2" + CFLAGS_linux=-I/usr/include/SDL2 LINKER_ARGS_linux="-L/usr/lib/x86_64-linux-gnu -lSDL2" COMPILER_ARGS_linux= + CFLAGS_iphone=-I$DEPS/target-sysroot-2-darwin-ios/System/Library/Frameworks/SDL2.framework/Headers LINKER_ARGS_iphone="-framework SDL2 \ -framework AVFoundation -framework CoreGraphics -framework CoreMotion -framework Foundation -framework GameController \ -framework AudioToolbox -framework OpenGLES -framework QuartzCore -framework UIKit" COMPILER_ARGS_iphone=-nomain + CFLAGS_raspberrypi=-I$DEPS/target-sysroot-1-raspberrypi/usr/include/SDL2 LINKER_ARGS_raspberrypi="-lSDL2" +COMPILER_ARGS_raspberrypi=-nomain if [ x$TARGET == x ]; then case "$OSTYPE" in