Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swift binding integration #23

Merged
merged 6 commits into from Apr 25, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -16,6 +16,12 @@ endif()
include(CMakeParseArguments)
include(CheckCXXCompilerFlag)

# Get the SDK path for OSX.
execute_process(
COMMAND xcrun --sdk macosx --show-sdk-path
OUTPUT_VARIABLE CMAKE_OSX_SYSROOT
OUTPUT_STRIP_TRAILING_WHITESPACE)

project(llbuild)

# Add path for custom modules
@@ -62,6 +68,29 @@ set(LLBUILD_LIBRARY_OUTPUT_INTDIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR
find_package(Lit REQUIRED)
find_package(FileCheck REQUIRED)

# Fine swiftc on OSX using `xcrun --find swiftc` and `find_package` on Linux.
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
execute_process(
COMMAND xcrun --find swiftc
OUTPUT_VARIABLE SWIFTC_EXECUTABLE
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(SWIFTC_FOUND TRUE)
else()
find_package(Swiftc QUIET)
endif()

# Check if we have correct swift version for bindings.
if (SWIFTC_FOUND)
execute_process(
COMMAND ${SWIFTC_EXECUTABLE} --version
OUTPUT_VARIABLE SWIFTC_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE)
if (NOT SWIFTC_VERSION MATCHES "(.*)Swift version 3.0(.*)")
set(SWIFTC_EXECUTABLE)
set(SWIFTC_FOUND FALSE)
endif()
endif()

###
# Setup compiler and project build settings

@@ -0,0 +1,16 @@
# Usage: find_package(Swiftc)
#
# If successful the following variables will be defined
# SWIFTC_FOUND
# SWIFTC_EXECUTABLE

find_program(SWIFTC_EXECUTABLE
NAMES swiftc
DOC "Path to 'swiftc' executable")
Copy link
Member

@ddunbar ddunbar Apr 25, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to check and record the TOOLCHAINS and SDKROOT in use... if not, then what will happen when run as:

env TOOLCHAINS=swift cmake ...

is we will just find /usr/bin/swiftc as the swiftc, but if we run again with a different TOOLCHAINS variable it won't work. I'd prefer if the configure step would bake in the value for TOOLCHAINS, I think.

Copy link
Member Author

@aciidb0mb3r aciidb0mb3r Apr 25, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed this by finding swiftc by xcrun --find swiftc on darwin


# Handle REQUIRED and QUIET arguments, this will also set SWIFTC_FOUND to true
# if SWIFTC_EXECUTABLE exists.
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Swiftc
"Failed to locate 'swiftc' executable"
SWIFTC_EXECUTABLE)
@@ -111,3 +111,98 @@ function(add_unittest test_suite test_name)

set_property(TARGET ${test_name} APPEND PROPERTY COMPILE_DEFINITIONS GTEST_HAS_RTTI=0)
endfunction()

# Compile swift sources to a dynamic framework.
# Usage:
# target # Target name
# name # Swift Module name
# deps # Target dependencies
# sources # List of sources
# additional_args # List of additional args to pass
function(add_swift_module target name deps sources additional_args)

set(BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY})

list(APPEND ARGS -module-name ${name})
list(APPEND ARGS -incremental -emit-dependencies -emit-module)
list(APPEND ARGS -emit-module-path ${name}.swiftmodule)

set(FILEMAP ${BUILD_DIR}/output-file-map.json)
set(OUTPUT_FILE_MAP ${FILEMAP})

# Remove old file and start writing new one.
file(REMOVE ${FILEMAP})
file(APPEND ${FILEMAP} "{\n")
foreach(source ${sources})
file(APPEND ${FILEMAP} "\"${CMAKE_CURRENT_SOURCE_DIR}/${source}\": {\n")
file(APPEND ${FILEMAP} "\"dependencies\": \"${BUILD_DIR}/${source}.d\",\n")
set(OBJECT ${BUILD_DIR}/${source}.o)
list(APPEND OUTPUTS ${OBJECT})
file(APPEND ${FILEMAP} "\"object\": \"${OBJECT}\",\n")
file(APPEND ${FILEMAP} "\"swiftmodule\": \"${BUILD_DIR}/${source}~partial.swiftmodule\",\n")
file(APPEND ${FILEMAP} "\"swift-dependencies\": \"${BUILD_DIR}/${source}.swiftdeps\"\n},\n")
endforeach()
file(APPEND ${FILEMAP} "\"\": {\n")
file(APPEND ${FILEMAP} "\"swift-dependencies\": \"${BUILD_DIR}/master.swiftdeps\"\n")
file(APPEND ${FILEMAP} "}\n")
file(APPEND ${FILEMAP} "}")

list(APPEND ARGS -output-file-map ${OUTPUT_FILE_MAP})
list(APPEND ARGS -parse-as-library)
list(APPEND ARGS -c)

foreach(source ${sources})
list(APPEND ARGS ${CMAKE_CURRENT_SOURCE_DIR}/${source})
endforeach()

# FIXME: Find a better way to handle build types.
if (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
list(APPEND ARGS -g)
endif()
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
list(APPEND ARGS -Onone -g)
else()
list(APPEND ARGS -O -whole-module-optimization)
endif()

foreach(arg ${additional_args})
list(APPEND ARGS ${arg})
endforeach()

if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
list(APPEND ARGS -sdk ${CMAKE_OSX_SYSROOT})
endif()

# Compile swiftmodule.
add_custom_command(
OUTPUT ${OUTPUTS}
COMMAND swiftc
ARGS ${ARGS}
DEPENDS ${sources}
)

# Link and create dynamic framework.
set(DYLIB_OUTPUT ${LLBUILD_LIBRARY_OUTPUT_INTDIR}/${target}.dylib)

if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
list(APPEND DYLYB_ARGS -sdk ${CMAKE_OSX_SYSROOT})
endif()

list(APPEND DYLYB_ARGS -module-name ${name})
list(APPEND DYLYB_ARGS -o ${DYLIB_OUTPUT})
list(APPEND DYLYB_ARGS -emit-library ${OUTPUTS})
foreach(arg ${additional_args})
list(APPEND DYLYB_ARGS ${arg})
endforeach()
list(APPEND DYLYB_ARGS -L ${LLBUILD_LIBRARY_OUTPUT_INTDIR})

add_custom_command(
OUTPUT ${DYLIB_OUTPUT}
COMMAND swiftc
ARGS ${DYLYB_ARGS}
DEPENDS ${OUTPUTS}
)

# Add the target.
add_custom_target(${target} ALL DEPENDS ${deps} ${DYLIB_OUTPUT} ${sources})
endfunction()
@@ -0,0 +1,93 @@
import llbuild

typealias Compute = [Int] -> Int

class SimpleTask: Task {
let inputs: [Key]
var values: [Int]
let compute: Compute

init(_ inputs: [Key], compute: Compute) {
self.inputs = inputs
values = [Int](repeating: 0, count: inputs.count)
self.compute = compute
}

func start(_ engine: TaskBuildEngine) {
for (idx, input) in inputs.enumerated() {
engine.taskNeedsInput(input, inputID: idx)
}
}

func provideValue(_ engine: TaskBuildEngine, inputID: Int, value: Value) {
values[inputID] = Int(value.toString())!
}

func inputsAvailable(_ engine: TaskBuildEngine) {
let result = compute(values)
engine.taskIsComplete(Value("\(result)"), forceChange: false)
}
}

class SimpleBuildEngineDelegate: BuildEngineDelegate {
var builtKeys = [Key]()

func lookupRule(_ key: Key) -> Rule {
switch key.toString() {
case "A":
return SimpleRule([]) { arr in
precondition(self.builtKeys.isEmpty)
self.builtKeys.append(key)
return 2
}
case "B":
return SimpleRule([]) { arr in
precondition(self.builtKeys.count == 1)
self.builtKeys.append(key)
return 3
}
case "C":
return SimpleRule([Key("A"), Key("B")]) { arr in
precondition(self.builtKeys.count == 2)
precondition(self.builtKeys[0].toString() == "A")
precondition(self.builtKeys[1].toString() == "B")
self.builtKeys.append(key)
return arr[0] * arr[1]
}
default: fatalError("Unexpected key \(key) lookup")
}
}
}

class SimpleRule: Rule {
let inputs: [Key]
let compute: Compute
init(_ inputs: [Key], compute: Compute) {
self.inputs = inputs
self.compute = compute
}
func createTask() -> Task {
return SimpleTask(inputs, compute: compute)
}
}

let delegate = SimpleBuildEngineDelegate()
var engine = BuildEngine(delegate: delegate)

// C depends on A and B
var result = engine.build(key: Key("C"))
print("\(result.toString())")

precondition(result.toString() == "6")

// Make sure building already built keys do not re-compute.
delegate.builtKeys.removeAll()
precondition(delegate.builtKeys.isEmpty)

result = engine.build(key: Key("A"))
precondition(result.toString() == "2")
precondition(delegate.builtKeys.isEmpty)

result = engine.build(key: Key("B"))
precondition(result.toString() == "3")
precondition(delegate.builtKeys.isEmpty)
@@ -1,2 +1,11 @@
add_custom_target(swift-bindings
DEPENDS libllbuild)
# Set sources.
set(SOURCES llbuild.swift)

# Link C API.
list(APPEND additional_args -import-underlying-module -lllbuild)
list(APPEND additional_args -I ${CMAKE_CURRENT_SOURCE_DIR}/../libllbuild/public-api)

# Add swift bindings target if swift compiler is present.
if (SWIFTC_FOUND)
add_swift_module(swift-bindings llbuild libllbuild "${SOURCES}" "${additional_args}")
endif()
@@ -9,7 +9,6 @@
// This file contains Swift bindings for the llbuild C API.

import Foundation
import llbuild

enum DatabaseError: ErrorProtocol {
case AttachFailure(message: String)
@@ -33,7 +33,7 @@ if(PYTHONINTERP_FOUND)
--param build_mode=${build_mode})

set(test_target_dependencies
llbuild libllbuild swift-build-tool UnitTests)
llbuild libllbuild swift-bindings swift-build-tool UnitTests)

add_custom_target(test-llbuild
COMMAND ${lit_command} ${CMAKE_CURRENT_BINARY_DIR}
@@ -1 +1 @@
config.suffixes = ['.llbuild']
config.suffixes = ['.txt', '.llbuild']
@@ -0,0 +1,8 @@
# Check basic 'core' functionality of the swift bindings
#
# REQUIRES: has-swift=TRUE
# RUN: env LD_LIBRARY_PATH=%{llbuild-lib-dir} %{swiftc} %{swiftc-platform-flags} %{srcroot}/examples/swift-bindings/core/basic.swift -I %{srcroot}/build/products/swift-bindings -I %{srcroot}/products/libllbuild/public-api -Xlinker %{llbuild-lib-dir}/swift-bindings.dylib -o %t.exe
# RUN: env LD_LIBRARY_PATH=%{llbuild-lib-dir} %t.exe %s > %t.out
# RUN: cat %t.out
# RUN: %{FileCheck} %s --input-file %t.out
# CHECK: 6
Copy link
Member

@ddunbar ddunbar Apr 25, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit is missing one thing, there needs to be a dependency from the tests target to the Swift bindings target, otherwise they don't get built before running the tests.

You can see this if you do a build from clean.

Copy link
Member Author

@aciidb0mb3r aciidb0mb3r Apr 25, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -64,6 +64,9 @@ config.target_triple = None
# Add a platform feature.
config.available_features.add("platform="+platform.system())

# Add swiftc feature.
config.available_features.add("has-swift="+config.swiftc_found)

###

# Define our supported substitutions.
@@ -72,8 +75,10 @@ config.substitutions.append( ('%{llbuild}', "%r" % (
config.substitutions.append( ('%{swift-build-tool}', "%r" % (
os.path.join(llbuild_tools_dir, 'swift-build-tool'),)) )
config.substitutions.append( ('%{FileCheck}', config.filecheck_path) )
config.substitutions.append( ('%{swiftc}', config.swiftc_path) )
config.substitutions.append( ('%{llbuild-lib-dir}', llbuild_lib_dir) )
config.substitutions.append( ('%{srcroot}', llbuild_src_root) )
config.substitutions.append( ('%{swiftc-platform-flags}', "" if not config.osx_sysroot else "-sdk " + config.osx_sysroot) )

###

@@ -12,6 +12,9 @@ config.llbuild_lib_dir = "@LLBUILD_LIBS_DIR@"

# Tools found by the build system.
config.filecheck_path = "@FILECHECK_EXECUTABLE@"
config.swiftc_path = "@SWIFTC_EXECUTABLE@"
config.swiftc_found = "@SWIFTC_FOUND@"
config.osx_sysroot = "@CMAKE_OSX_SYSROOT@"

# Support substitution of the tools_dir with user parameters. This is
# used when we can't determine the tool dir at configuration time.