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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
16 changes: 16 additions & 0 deletions cmake/modules/FindSwiftc.cmake
Original file line number Diff line number Diff line change
@@ -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
Contributor

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
Contributor Author

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)
95 changes: 95 additions & 0 deletions cmake/modules/Utility.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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()
93 changes: 93 additions & 0 deletions examples/swift-bindings/core/basic.swift
Original file line number Diff line number Diff line change
@@ -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)
13 changes: 11 additions & 2 deletions products/swift-bindings/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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()
1 change: 0 additions & 1 deletion products/swift-bindings/llbuild.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
2 changes: 1 addition & 1 deletion tests/Examples/lit.local.cfg
Original file line number Diff line number Diff line change
@@ -1 +1 @@
config.suffixes = ['.llbuild']
config.suffixes = ['.txt', '.llbuild']
8 changes: 8 additions & 0 deletions tests/Examples/swift-bindings/core/basic.txt
Original file line number Diff line number Diff line change
@@ -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
Contributor

@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
Contributor Author

Choose a reason for hiding this comment

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

fixed

5 changes: 5 additions & 0 deletions tests/lit.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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) )

###

Expand Down
3 changes: 3 additions & 0 deletions tests/lit.site.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down