Shadowman
Shadowman

Reputation: 12079

"swift test" Fails With "executable" But Not "library"?

I am developing some server-side code in Swift on my Mac, but for eventual deployment on Linux. I have created my project structure using the Swift Package Manager. I've created a couple of XCTestCase unit tests for classes. I've noticed, however, that if I set my target product to .executable, when I run swift test I get output similar to:

Compile Swift Module 'MyProjUtilTests' (3 sources) Linking ./.build/x86_64-apple-macosx10.10/debug/MyProjUtilPackageTests.xctest/Contents/MacOS/MyProjUtilPackageTests Undefined symbols for architecture x86_64:
"__T012MyProjUtil16PropertyListUtilCMa", referenced from: __T017MyProjUtilTests20PropertyListUtilTestC16testReadFromFileyyF in PropertyListUtilTest.swift.o "__T012MyProjUtil3FooCMa", referenced from: __T017MyProjUtilTests03FooC0C07testRunD0yyFSSyKXKfu_ in FooTests.swift.o "__T012MyProjUtilAAVABycfC", referenced from: __T017MyProjUtilTestsAAC11testExampleyyFSSyKXKfu_ in MyProjUtilTests.swift.o ld: symbol(s) not found for architecture x86_64 :0: error: link command failed with exit code 1 (use -v to see invocation) error: terminated(1): /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-build-tool -f /Volumes/Untitled/SwiftDev/MyProj/MyProjUtil/.build/debug.yaml test

However, if I change my target product to .library, and run swift test it executes my tests and exits without an error. Is this a bug in the Swift Package Manager? Do I need to do something else in order to include unit tests in my executable product? Or is this the expected behavior? Can anyone shed some light on what I'm seeing here and why? Thanks!!

Upvotes: 2

Views: 513

Answers (1)

Daniel R
Daniel R

Reputation: 312

Yes, this is expected behavior.

You could make three targets in your Package.swift. "MyAppLibTests", "MyAppLib" and a "MyAppExec".

"MyAppLibTests" depends on "MyAppLib". "MyAppExec" also depends on "MyAppLib". And "MyAppLib" depends on whatever external dependencies you might have.

Your directory structure could look like:

+-- Package.swift
|
+-- Sources/
| |
| +-- MyAppLib/
| |     ...files...
| |
| +-- MyAppLibExec/
|   |
|   +-- main.swift
|
+-- Tests/
  |
  +-- MyAppLibTests/
    |
    +-- MyTestCase.swift

And then in your main.swift, you would import MyAppLib; import Foundation and then perhaps parse the commandline arguments and environment variables. Pass the options into your library's main entry point. Something like MyAppMain().run(option: parsedValue, option2: parsedValue2)

Bonus: to get a commandline argument, they are in an array of Strings: Commandline.arguments. To get Environment variables, they are in a Dictionary of Strings to Strings: ProcessInfo.processInfo.environment["SOME_ENV_VAR"]

Upvotes: 3

Related Questions