Skip to content

Commit d3373dc

Browse files
aciidghddunbar
authored andcommitted
Swift binding integration (#23)
* [swift-bindings] Compile swift bindings for OSX * [swift-bindings] Add a basic example and test for core * [swift-bindings] Find swiftc path using cmake for lit * [swift-bindings] linux fixes * [swift-bindings] check if swift version > 3 * [swift-bindings] find swiftc using xcrun on OSX
1 parent b52e481 commit d3373dc

File tree

11 files changed

+262
-5
lines changed

11 files changed

+262
-5
lines changed

CMakeLists.txt

+29
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ endif()
1616
include(CMakeParseArguments)
1717
include(CheckCXXCompilerFlag)
1818

19+
# Get the SDK path for OSX.
20+
execute_process(
21+
COMMAND xcrun --sdk macosx --show-sdk-path
22+
OUTPUT_VARIABLE CMAKE_OSX_SYSROOT
23+
OUTPUT_STRIP_TRAILING_WHITESPACE)
24+
1925
project(llbuild)
2026

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

71+
# Fine swiftc on OSX using `xcrun --find swiftc` and `find_package` on Linux.
72+
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
73+
execute_process(
74+
COMMAND xcrun --find swiftc
75+
OUTPUT_VARIABLE SWIFTC_EXECUTABLE
76+
OUTPUT_STRIP_TRAILING_WHITESPACE)
77+
set(SWIFTC_FOUND TRUE)
78+
else()
79+
find_package(Swiftc QUIET)
80+
endif()
81+
82+
# Check if we have correct swift version for bindings.
83+
if (SWIFTC_FOUND)
84+
execute_process(
85+
COMMAND ${SWIFTC_EXECUTABLE} --version
86+
OUTPUT_VARIABLE SWIFTC_VERSION
87+
OUTPUT_STRIP_TRAILING_WHITESPACE)
88+
if (NOT SWIFTC_VERSION MATCHES "(.*)Swift version 3.0(.*)")
89+
set(SWIFTC_EXECUTABLE)
90+
set(SWIFTC_FOUND FALSE)
91+
endif()
92+
endif()
93+
6594
###
6695
# Setup compiler and project build settings
6796

cmake/modules/FindSwiftc.cmake

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Usage: find_package(Swiftc)
2+
#
3+
# If successful the following variables will be defined
4+
# SWIFTC_FOUND
5+
# SWIFTC_EXECUTABLE
6+
7+
find_program(SWIFTC_EXECUTABLE
8+
NAMES swiftc
9+
DOC "Path to 'swiftc' executable")
10+
11+
# Handle REQUIRED and QUIET arguments, this will also set SWIFTC_FOUND to true
12+
# if SWIFTC_EXECUTABLE exists.
13+
include(FindPackageHandleStandardArgs)
14+
find_package_handle_standard_args(Swiftc
15+
"Failed to locate 'swiftc' executable"
16+
SWIFTC_EXECUTABLE)

cmake/modules/Utility.cmake

+95
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,98 @@ function(add_unittest test_suite test_name)
111111

112112
set_property(TARGET ${test_name} APPEND PROPERTY COMPILE_DEFINITIONS GTEST_HAS_RTTI=0)
113113
endfunction()
114+
115+
# Compile swift sources to a dynamic framework.
116+
# Usage:
117+
# target # Target name
118+
# name # Swift Module name
119+
# deps # Target dependencies
120+
# sources # List of sources
121+
# additional_args # List of additional args to pass
122+
function(add_swift_module target name deps sources additional_args)
123+
124+
set(BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY})
125+
126+
list(APPEND ARGS -module-name ${name})
127+
list(APPEND ARGS -incremental -emit-dependencies -emit-module)
128+
list(APPEND ARGS -emit-module-path ${name}.swiftmodule)
129+
130+
set(FILEMAP ${BUILD_DIR}/output-file-map.json)
131+
set(OUTPUT_FILE_MAP ${FILEMAP})
132+
133+
# Remove old file and start writing new one.
134+
file(REMOVE ${FILEMAP})
135+
file(APPEND ${FILEMAP} "{\n")
136+
foreach(source ${sources})
137+
file(APPEND ${FILEMAP} "\"${CMAKE_CURRENT_SOURCE_DIR}/${source}\": {\n")
138+
file(APPEND ${FILEMAP} "\"dependencies\": \"${BUILD_DIR}/${source}.d\",\n")
139+
set(OBJECT ${BUILD_DIR}/${source}.o)
140+
list(APPEND OUTPUTS ${OBJECT})
141+
file(APPEND ${FILEMAP} "\"object\": \"${OBJECT}\",\n")
142+
file(APPEND ${FILEMAP} "\"swiftmodule\": \"${BUILD_DIR}/${source}~partial.swiftmodule\",\n")
143+
file(APPEND ${FILEMAP} "\"swift-dependencies\": \"${BUILD_DIR}/${source}.swiftdeps\"\n},\n")
144+
endforeach()
145+
file(APPEND ${FILEMAP} "\"\": {\n")
146+
file(APPEND ${FILEMAP} "\"swift-dependencies\": \"${BUILD_DIR}/master.swiftdeps\"\n")
147+
file(APPEND ${FILEMAP} "}\n")
148+
file(APPEND ${FILEMAP} "}")
149+
150+
list(APPEND ARGS -output-file-map ${OUTPUT_FILE_MAP})
151+
list(APPEND ARGS -parse-as-library)
152+
list(APPEND ARGS -c)
153+
154+
foreach(source ${sources})
155+
list(APPEND ARGS ${CMAKE_CURRENT_SOURCE_DIR}/${source})
156+
endforeach()
157+
158+
# FIXME: Find a better way to handle build types.
159+
if (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
160+
list(APPEND ARGS -g)
161+
endif()
162+
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
163+
list(APPEND ARGS -Onone -g)
164+
else()
165+
list(APPEND ARGS -O -whole-module-optimization)
166+
endif()
167+
168+
foreach(arg ${additional_args})
169+
list(APPEND ARGS ${arg})
170+
endforeach()
171+
172+
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
173+
list(APPEND ARGS -sdk ${CMAKE_OSX_SYSROOT})
174+
endif()
175+
176+
# Compile swiftmodule.
177+
add_custom_command(
178+
OUTPUT ${OUTPUTS}
179+
COMMAND swiftc
180+
ARGS ${ARGS}
181+
DEPENDS ${sources}
182+
)
183+
184+
# Link and create dynamic framework.
185+
set(DYLIB_OUTPUT ${LLBUILD_LIBRARY_OUTPUT_INTDIR}/${target}.dylib)
186+
187+
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
188+
list(APPEND DYLYB_ARGS -sdk ${CMAKE_OSX_SYSROOT})
189+
endif()
190+
191+
list(APPEND DYLYB_ARGS -module-name ${name})
192+
list(APPEND DYLYB_ARGS -o ${DYLIB_OUTPUT})
193+
list(APPEND DYLYB_ARGS -emit-library ${OUTPUTS})
194+
foreach(arg ${additional_args})
195+
list(APPEND DYLYB_ARGS ${arg})
196+
endforeach()
197+
list(APPEND DYLYB_ARGS -L ${LLBUILD_LIBRARY_OUTPUT_INTDIR})
198+
199+
add_custom_command(
200+
OUTPUT ${DYLIB_OUTPUT}
201+
COMMAND swiftc
202+
ARGS ${DYLYB_ARGS}
203+
DEPENDS ${OUTPUTS}
204+
)
205+
206+
# Add the target.
207+
add_custom_target(${target} ALL DEPENDS ${deps} ${DYLIB_OUTPUT} ${sources})
208+
endfunction()
+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import llbuild
2+
3+
typealias Compute = [Int] -> Int
4+
5+
class SimpleTask: Task {
6+
let inputs: [Key]
7+
var values: [Int]
8+
let compute: Compute
9+
10+
init(_ inputs: [Key], compute: Compute) {
11+
self.inputs = inputs
12+
values = [Int](repeating: 0, count: inputs.count)
13+
self.compute = compute
14+
}
15+
16+
func start(_ engine: TaskBuildEngine) {
17+
for (idx, input) in inputs.enumerated() {
18+
engine.taskNeedsInput(input, inputID: idx)
19+
}
20+
}
21+
22+
func provideValue(_ engine: TaskBuildEngine, inputID: Int, value: Value) {
23+
values[inputID] = Int(value.toString())!
24+
}
25+
26+
func inputsAvailable(_ engine: TaskBuildEngine) {
27+
let result = compute(values)
28+
engine.taskIsComplete(Value("\(result)"), forceChange: false)
29+
}
30+
}
31+
32+
class SimpleBuildEngineDelegate: BuildEngineDelegate {
33+
var builtKeys = [Key]()
34+
35+
func lookupRule(_ key: Key) -> Rule {
36+
switch key.toString() {
37+
case "A":
38+
return SimpleRule([]) { arr in
39+
precondition(self.builtKeys.isEmpty)
40+
self.builtKeys.append(key)
41+
return 2
42+
}
43+
case "B":
44+
return SimpleRule([]) { arr in
45+
precondition(self.builtKeys.count == 1)
46+
self.builtKeys.append(key)
47+
return 3
48+
}
49+
case "C":
50+
return SimpleRule([Key("A"), Key("B")]) { arr in
51+
precondition(self.builtKeys.count == 2)
52+
precondition(self.builtKeys[0].toString() == "A")
53+
precondition(self.builtKeys[1].toString() == "B")
54+
self.builtKeys.append(key)
55+
return arr[0] * arr[1]
56+
}
57+
default: fatalError("Unexpected key \(key) lookup")
58+
}
59+
}
60+
}
61+
62+
class SimpleRule: Rule {
63+
let inputs: [Key]
64+
let compute: Compute
65+
init(_ inputs: [Key], compute: Compute) {
66+
self.inputs = inputs
67+
self.compute = compute
68+
}
69+
func createTask() -> Task {
70+
return SimpleTask(inputs, compute: compute)
71+
}
72+
}
73+
74+
let delegate = SimpleBuildEngineDelegate()
75+
var engine = BuildEngine(delegate: delegate)
76+
77+
// C depends on A and B
78+
var result = engine.build(key: Key("C"))
79+
print("\(result.toString())")
80+
81+
precondition(result.toString() == "6")
82+
83+
// Make sure building already built keys do not re-compute.
84+
delegate.builtKeys.removeAll()
85+
precondition(delegate.builtKeys.isEmpty)
86+
87+
result = engine.build(key: Key("A"))
88+
precondition(result.toString() == "2")
89+
precondition(delegate.builtKeys.isEmpty)
90+
91+
result = engine.build(key: Key("B"))
92+
precondition(result.toString() == "3")
93+
precondition(delegate.builtKeys.isEmpty)
+11-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,11 @@
1-
add_custom_target(swift-bindings
2-
DEPENDS libllbuild)
1+
# Set sources.
2+
set(SOURCES llbuild.swift)
3+
4+
# Link C API.
5+
list(APPEND additional_args -import-underlying-module -lllbuild)
6+
list(APPEND additional_args -I ${CMAKE_CURRENT_SOURCE_DIR}/../libllbuild/public-api)
7+
8+
# Add swift bindings target if swift compiler is present.
9+
if (SWIFTC_FOUND)
10+
add_swift_module(swift-bindings llbuild libllbuild "${SOURCES}" "${additional_args}")
11+
endif()

products/swift-bindings/llbuild.swift

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
// This file contains Swift bindings for the llbuild C API.
1010

1111
import Foundation
12-
import llbuild
1312

1413
enum DatabaseError: ErrorProtocol {
1514
case AttachFailure(message: String)

tests/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ if(PYTHONINTERP_FOUND)
3333
--param build_mode=${build_mode})
3434

3535
set(test_target_dependencies
36-
llbuild libllbuild swift-build-tool UnitTests)
36+
llbuild libllbuild swift-bindings swift-build-tool UnitTests)
3737

3838
add_custom_target(test-llbuild
3939
COMMAND ${lit_command} ${CMAKE_CURRENT_BINARY_DIR}

tests/Examples/lit.local.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
config.suffixes = ['.llbuild']
1+
config.suffixes = ['.txt', '.llbuild']
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Check basic 'core' functionality of the swift bindings
2+
#
3+
# REQUIRES: has-swift=TRUE
4+
# 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
5+
# RUN: env LD_LIBRARY_PATH=%{llbuild-lib-dir} %t.exe %s > %t.out
6+
# RUN: cat %t.out
7+
# RUN: %{FileCheck} %s --input-file %t.out
8+
# CHECK: 6

tests/lit.cfg

+5
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ config.target_triple = None
6464
# Add a platform feature.
6565
config.available_features.add("platform="+platform.system())
6666

67+
# Add swiftc feature.
68+
config.available_features.add("has-swift="+config.swiftc_found)
69+
6770
###
6871

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

7883
###
7984

tests/lit.site.cfg.in

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ config.llbuild_lib_dir = "@LLBUILD_LIBS_DIR@"
1212

1313
# Tools found by the build system.
1414
config.filecheck_path = "@FILECHECK_EXECUTABLE@"
15+
config.swiftc_path = "@SWIFTC_EXECUTABLE@"
16+
config.swiftc_found = "@SWIFTC_FOUND@"
17+
config.osx_sysroot = "@CMAKE_OSX_SYSROOT@"
1518

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

0 commit comments

Comments
 (0)