Skip to content

Commit 7e285fa

Browse files
committed
Add basic support for building C via swiftpm. As of now assumes module.modulemap will be available inside a include directory, doesn't generates it automatically. Also creates only shared libraries and not executable for now.
1 parent bd97ae1 commit 7e285fa

File tree

10 files changed

+89
-10
lines changed

10 files changed

+89
-10
lines changed

Sources/Build/describe().swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,40 @@ public func describe(prefix: String, _ conf: Configuration, _ modules: [Module],
103103
}
104104
}
105105

106+
//For C language Modules
107+
//FIXME: Probably needs more compiler options for debug and release modes
108+
//FIXME: Incremental builds
109+
//FIXME: Add support for executables
110+
for case let module as CLangModule in modules {
111+
let inputs = module.dependencies.map{ $0.targetName } + module.sources.paths
112+
let productPath = Path.join(prefix, "\(module.c99name).o")
113+
let wd = Path.join(prefix, "\(module.c99name).build")
114+
mkdirs.insert(wd)
115+
116+
var args: [String] = []
117+
args += ["-fmodules", "-fmodule-name=\(module.name)"]
118+
args += ["-fmodule-map-file=\(module.moduleMapPath)", "-working-directory", Path.join(prefix, "\(module.c99name).build")]
119+
120+
switch conf {
121+
case .Debug:
122+
args += ["-g", "-O0"]
123+
case .Release:
124+
args += ["-O2"]
125+
}
126+
127+
args += module.sources.paths
128+
args += ["-shared", "-o", Path.join(prefix, "lib\(module.c99name).so")]
129+
130+
let clang = ShellTool(
131+
description: "Compiling \(module.name)",
132+
inputs: inputs,
133+
outputs: [productPath, module.targetName],
134+
args: [Resources.path.clang] + args)
135+
136+
let command = Command(name: module.targetName, tool: clang)
137+
append(command, buildable: module)
138+
}
139+
106140
// make eg .build/debug/foo.build/subdir for eg. Sources/foo/subdir/bar.swift
107141
// TODO swift-build-tool should do this
108142
for dir in mkdirs {
@@ -171,6 +205,7 @@ public func describe(prefix: String, _ conf: Configuration, _ modules: [Module],
171205
}
172206
args += platformArgs() //TODO don't need all these here or above: split outname
173207
args += Xld
208+
args += ["-L\(prefix)"]
174209
args += ["-o", outpath]
175210
args += objects
176211

Sources/Build/misc.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ extension Module {
3232
if let module = module as? CModule {
3333
let moduleMapPath = Path.join(module.path, "module.modulemap")
3434
return ["-Xcc", "-fmodule-map-file=\(moduleMapPath)"]
35+
} else if let cmodule = module as? CLangModule {
36+
return ["-Xcc", "-fmodule-map-file=\(cmodule.moduleMapPath)"]
3537
} else {
3638
return []
3739
}

Sources/PackageType/Module.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ public class CModule: Module {
7070
}
7171
}
7272

73+
public class CLangModule: Module {
74+
public let sources: Sources
75+
public let moduleMapPath: String
76+
77+
public init(name: String, sources: Sources) {
78+
self.sources = sources
79+
//TODO: generate this with swiftpm if layout supports
80+
moduleMapPath = sources.root + "/include/module.modulemap"
81+
super.init(name: name)
82+
}
83+
}
84+
7385
public class TestModule: SwiftModule {
7486

7587
public init(basename: String, sources: Sources) {

Sources/PackageType/Sources.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
import Utility
1212

1313
public struct Sources {
14+
15+
static public var validExtensions: [String] {
16+
return [".swift", ".c"]
17+
}
18+
1419
public let relativePaths: [String]
1520
public let root: String
1621

Sources/Transmute/Error.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,6 @@ extension Package {
2626
extension Module {
2727
public enum Error: ErrorType {
2828
case NoSources(String)
29+
case MixedSources(String)
2930
}
3031
}

Sources/Transmute/Package+modules.swift

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,28 @@ extension Package {
3838
let modules: [Module]
3939
if maybeModules.isEmpty {
4040
do {
41-
modules = [SwiftModule(name: self.name, sources: try sourcify(srcroot))]
41+
let sourcified = try sourcify(srcroot)
42+
if sourcified.isSwiftModule {
43+
modules = [SwiftModule(name: self.name, sources: sourcified.sources)]
44+
} else {
45+
modules = [CLangModule(name: self.name, sources: sourcified.sources)]
46+
}
47+
4248
} catch Module.Error.NoSources {
4349
throw ModuleError.NoModules(self)
4450
}
4551
} else {
46-
modules = try maybeModules.map(sourcify).map { sources in
52+
modules = try maybeModules.map(sourcify).map { sourcified in
4753
let name: String
48-
if sources.root == srcroot {
54+
if sourcified.sources.root == srcroot {
4955
name = self.name
5056
} else {
51-
name = sources.root.basename
57+
name = sourcified.sources.root.basename
58+
}
59+
if sourcified.isSwiftModule {
60+
return SwiftModule(name: name, sources: sourcified.sources)
5261
}
53-
return SwiftModule(name: name, sources: sources)
62+
return CLangModule(name: name, sources: sourcified.sources)
5463
}
5564
}
5665

@@ -75,18 +84,26 @@ extension Package {
7584
return modules
7685
}
7786

78-
func sourcify(path: String) throws -> Sources {
87+
func sourcify(path: String) throws -> (sources: Sources, isSwiftModule: Bool) {
7988
let sources = walk(path, recursing: shouldConsiderDirectory).filter(isValidSource)
8089
guard sources.count > 0 else { throw Module.Error.NoSources(path) }
81-
return Sources(paths: sources, root: path)
90+
let partioned = sources.partition { $0.hasSuffix(".swift") }
91+
guard !(partioned.0.count > 0 && partioned.1.count > 0) else { throw Module.Error.MixedSources(path) }
92+
return (sources: Sources(paths: sources, root: path), isSwiftModule: partioned.0.count > 0)
8293
}
8394

8495
func isValidSource(path: String) -> Bool {
8596
if path.basename.hasPrefix(".") { return false }
8697
let path = path.normpath
8798
if path == manifest.path.normpath { return false }
8899
if excludes.contains(path) { return false }
89-
return path.lowercaseString.hasSuffix(".swift") && path.isFile
100+
if !path.isFile { return false }
101+
for ext in Sources.validExtensions {
102+
if path.lowercaseString.hasSuffix(ext) {
103+
return true
104+
}
105+
}
106+
return false
90107
}
91108

92109
private func targetForName(name: String) -> Target? {

Sources/Transmute/Package+shouldConsiderDirectory.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ extension Package {
1515
func shouldConsiderDirectory(path: String) -> Bool {
1616
let base = path.basename.lowercaseString
1717
if base == "tests" { return false }
18+
if base.hasSuffix("include") { return false }
1819
if base.hasSuffix(".xcodeproj") { return false }
1920
if base.hasSuffix(".playground") { return false }
2021
if base.hasPrefix(".") { return false } // eg .git

Sources/Transmute/Package+testModules.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ extension Package {
1717
//Don't try to walk Tests if it is in excludes
1818
if testsPath.isDirectory && excludes.contains(testsPath) { return [] }
1919
return walk(testsPath, recursively: false).filter(shouldConsiderDirectory).flatMap { dir in
20-
if let sources = try? self.sourcify(dir) {
21-
return TestModule(basename: dir.basename, sources: sources)
20+
if let sourcified = try? self.sourcify(dir) {
21+
return TestModule(basename: dir.basename, sources: sourcified.sources)
2222
} else {
2323
print("warning: no sources in test module: \(path)")
2424
return nil

Sources/Transmute/fillModuleGraph().swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ func fillModuleGraph(packages: [Package], modulesForPackage: (Package) -> [Modul
2020
return false
2121
case let module as SwiftModule where module.type == .Library:
2222
return true
23+
case is CLangModule:
24+
return true
2325
case is CModule:
2426
return true
2527
default:

Sources/Utility/Resources.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ public final class Resources {
5858
public static var swiftc: String = {
5959
return getenv("SWIFT_EXEC") ?? Resources.findExecutable("swiftc")
6060
}()
61+
62+
public static var clang: String = {
63+
return getenv("CLANG_EXEC") ?? Resources.findExecutable("clang")
64+
}()
6165

6266
public static var swift_build_tool: String = {
6367
return getenv("SWIFT_BUILD_TOOL") ?? Resources.findExecutable("swift-build-tool")

0 commit comments

Comments
 (0)