Package builds on CommandLine but not through generated xcodeproj file

I have run into an issue where SPM is not capable of generating a xcodeproj file that compiles.

I have created a test project (spm-demo that shows the problem. The project can be build on the command line and works as expected. The generated xcodeproj file does not work which makes that I cannot use debugging anymore.

See the demo project which contains instruction on how to re-create the problem.

I haven’t actually tried your demonstration, but I suspect SwiftGD may be missing some linker flags it needs to properly use GD. Have a look at the discussion in this other thread starting here and see if the error messages look similar.

Thanks for responding. I don't think this is similar. In my case I get compilation errors not linking errors.

.../demo-direct/.build/checkouts/SwiftGD/Sources/SwiftGD/Format.swift:19:44: Use of undeclared type 'gdImagePtr'
.../demo-direct/.build/checkouts/SwiftGD/Sources/SwiftGD/Format.swift:29:28: Use of undeclared type 'gdImagePtr'
.../demo-direct/.build/checkouts/SwiftGD/Sources/SwiftGD/Format.swift:40:77: Use of undeclared type 'gdImagePtr'
.../demo-direct/.build/checkouts/SwiftGD/Sources/SwiftGD/Format.swift:46:32: Use of undeclared type 'gdImagePtr'
.../demo-direct/.build/checkouts/SwiftGD/Sources/SwiftGD/Format.swift:55:32: Use of undeclared type 'gdImagePtr'

etc.

Just to be 100% certain since it passes a CFLAG I created a xcconfig file with this content:

OTHER_LDFLAGS = -lgd
OTHER_CFLAGS = -DSWIFT_PACKAGE

which did not lead to a solution.

I want to add to my issue. I found out that if I just take the package SwiftGD the problem shows in that single package as well:

git clone https://github.com/twostraws/swiftgd
cd swiftgd
swift build # This works
swift package generate-xcodeproj
open SwiftGD.xcodeproj/

and then try to build in Xcode it fails. Again it seems like the underlying modulemap is not "executed".

.../swiftgd/Sources/SwiftGD/Format.swift:19:44: Use of undeclared type 'gdImagePtr'
.../swiftgd/Sources/SwiftGD/Format.swift:29:28: Use of undeclared type 'gdImagePtr'
.../swiftgd/Sources/SwiftGD/Format.swift:40:77: Use of undeclared type 'gdImagePtr'

Edit: I’m an idiot who cannot read clearly. You actually said that did not fix it. I’ll leave my original post below for anyone else who arrives here by searching.


If that fixed it, the issue actually is the same. The need for this line:

OTHER_LDFLAGS = -lgd

...means that package is missing the linker setting in it’s package manifest:

.target(
    name: "...",
    linkerSettings: [.linkedLibrary("gd")]),

And this line:

OTHER_CFLAGS = -DSWIFT_PACKAGE

...means that package is missing the C setting in it’s package manifest:

.target(
    name: "...",
    cSettings: [.define("SWIFT_PACKAGE")]),

You just beat me to my answer that it didn't fix it :smile:

I tinkered around some more, but I couldn’t figure it out. It seems to me that SwiftGD is relying on something not declared in the manifest, and it just gets lucky that it works from the command line. Possibly it is something accidentally inherited from the terminal’s shell environment, which Xcode’s build process does not see.

Somehow I think this is something coming from the OS and not from the environment. This problem wasn't there initially. I think (cannot say for certain) that it started with the last MacOS update. (I'm now on 10.14.5)

I checked my environment variables and don't see anything suspect.

My guess is that it has to do with the support of system libraries in the generated xcodeproj file. The package SwiftGD has a dependency on an externally defined library:

        .systemLibrary(name: "gd", pkgConfig: "gdlib", providers: [.apt(["libgd-dev"]), .brew(["gd"])]),

The compiler errors I get are symbols that should come from the gd.h file coming from that package. Interestingly enough there is no error/warning for the "import gd" statement, it just complains about unknown types.

Is brew installing into /usr/local on your machine? If so, when you installed gd using brew it should have been installed into /usr/local. swift/clang/clang++ all include /usr/local/ as a system library location, by default, which is what you get if you use the command line to run the compiler without adding special compiler command options. However, Xcode puts all kinds of -isystem command line parameters which affects the "system" root paths, and /usr/local may not be searched, based on the what else is going on in the Xcode project that SPM generates. I know I often have to explicitly add /usr/local/include, /usr/local/lib, etc., to the relevant search paths in Xcode for C/C++/Obj-C/Obj-C++. I haven't run into this issue with Swift, but, my Swift usage tends to be either vanilla Swift, or using Apple frameworks. Has not been issue, yet.

Yes it is installed in /usr/local.

I just downloaded a fresh copy of SwiftGD and generated the xcodeproj file. (It still fails)
When I do a grep I see the following:

grep -i local SwiftGD.xcodeproj/project.pbxproj
               "-I/usr/local/Cellar/gd/2.2.5/include"
               "-L/usr/local/Cellar/gd/2.2.5/lib",
               "-I/usr/local/Cellar/gd/2.2.5/include"
               "-I/usr/local/Cellar/gd/2.2.5/include"
               "-L/usr/local/Cellar/gd/2.2.5/lib",
               "-I/usr/local/Cellar/gd/2.2.5/include"
               "-I/usr/local/Cellar/gd/2.2.5/include"
               "-L/usr/local/Cellar/gd/2.2.5/lib",
               "-I/usr/local/Cellar/gd/2.2.5/include"
               "-I/usr/local/Cellar/gd/2.2.5/include"
               "-L/usr/local/Cellar/gd/2.2.5/lib",
               "-I/usr/local/Cellar/gd/2.2.5/include"

which points in the right direction. Just to be certain I even reinstalled gd using brew.

As a point of interest, I experimented with SwiftGD, and I was able to get the framework to compile using Xcode, but, I had to change the path to the "gd" header in the module map from being an umbrella header, to a header pointing at the include file in /usr/local/include. I also added an "export *" line to the module map. I also added Sources/gd to the Header Search Path and Swift Import Paths build settings. I have not backed out the build settings, however, I think you would need to copy the module.modulemap file in Sources/gd to /usr/local/include, or wherever the gd.h file is located. Using the gd.h file in Sources/gd did not seem to work.

Started from an Xcode project generated using "swift package generate-xcodeproj"

Interesting. Does that mean that using the same name for the package and for the actual library should be discouraged (gd.h in this case). Fact of the matter is that the CommandLine just works as is.

I don't think so.

I changed the module.modulemap in Sources/gd to:

module gd {
    header "/usr/local/include/gd.h"
    link "gd"
}

and everything works on the command line ("swift build"), and with Xcode ("swift package generate-xcodeproj"; xcodebuild project=SwiftGD.xcodeproj"). Using Xcode generates a SwiftGD.framework that can be used in your application. If you need to be compatible with Linux, there would need to updates made to the module map, which you can look up on the Clang website (clang.llvm.org), which describes the module map language.

Apple folks have always cautioned about using umbrella headers in Apple environments. Not sure if this is an instance of that or not. Also, integration of Xcode and Swift Package Manager is a "work-in-progress" based on the discussions in the forums.

Hi Jonathan,

Great find but I don't think you understood what I meant. My thought was that the fact that the file /usr/local/include/gd.h and the file Sources/gd/gd.h have the same base name being gd.h.

So I made a different experiment:

I renamed gd.h in Sources/gd into xgd.h.

Sources/gd/
|-- module.modulemap
`-- xgd.h

And then I changed the content of module.modulemap in:

module gd {
	umbrella header "xgd.h"
	link "gd"
}

and guess what it now works on the CommandLine and in Xcode. Hence my statement by using the same name. (I just re-read my post and I agree that I did not explain that very well though :smile:)

So to repeat, perhaps a general point of advise is to make the header file name in the directory where the module.modulemap resides different from the system header file that needs to be included.

My guess is now that somehow the order of the includes is different or the semantics of #include "gd.h" vs #include <gd.h> are somehow lost.

I did not get that. Glad you found a way that works. Will keep in mind for my own projects.