is it possible to run CMake as part of a pre-build run phase script and compile the generated xcodeproj?

Hey All,


I'm wondering if someone could give me some help on something somewhat complex.


To start, I need to run you through the current project structure.

We have a CPP project which is built via

clang
via
xcodebuild
which is included as a submodule in the top-level iOS repo.

Initially, if you were to clone the repo and open it in

Xcode (11.3.1)
, you'd notice that the CPP xcodeproj is broken.


That is because there is a pre-build run-phase script which initialises the submodule, then runs

CMake
on it and generates that broken xcodeproj (it has other submodule dependencies of it's own, of course!) - if it hasn't already been, or if there is some force-rebuild flag set.


In Xcode, besides the slightly strange UX of no progress indicator without going to the Report Navigator, The CMake command executes just fine, but then the xcodebuild command that was kicked off does not build any of this recently added CPP code. However, some amount of restarting XCode and cleaning and clearing derived data generally does the trick and I get a green build.


I can, slightly unwillingly, just invoke that

CMake
command prior to doing anything in Xcode, but I would really like the process to remain as simple as possible (as in, as close to just CMD+B as possible - without complicating the workflow and jumping into a terminal process to run a bunch of stuff).


Does anyone know if it is possible to achieve a CMD+B with metabuild tools generating xcodeproj stuff as it goes?

Accepted Reply

OK. I get it. I saw CPP and thought C pre-processor.


I think one problem you have is that you absolutely have to have those static libraries available to initially construct the project. They can be deleted and re-built at any time, but you'll need them at first. They can be stubs, if necessary.


You should be able to do all of this with either aggregate targets and/or makefile-based targets. As long as the static libraries are specified somewhere in the build flow and there is some rule to build them, it should all work fine, given a suitably complicated project.


I don't know what is special about C++ that is more complex than anything else or would require multiple build passes. As long as your build system is designed correctly, one pass is all it should take. At one point in the past, I sat down with nothing but the gnu make manual and taught myself how to create makefiles from scratch. That might be a worthwhile approach in your case. Xcode can probably do it too, but like any GUI approach, you can lose track of the complexity when it is hidden six levels deep in a GUI build tool. Makefiles are more straightforward and visible.


You probably wouldn't like my build wrapper. The primary build script is Perl and the input is an XML definition of the dependencies. I never use any fresh clones. I only download archived versions that haven't changed. I couldn't manage this project otherwise. My approach to clones and branches isn't much different than Perl and XML. The latest, bleeding edges of projects have no value to me. I'm happy to let other people spin their wheels on that stuff while I build my own software.


Edit: OK, Apple. Why is the word b r a n c h i n g not allowed in the forums? That's strange.

Replies

Can you try again to explain that? I have no idea what you are saying. What is a CPP?


I have a large project with a bunch of open source projects, some autotools, some cmake, and some Google abominations. I couldn't imagine trying to do everything, or anything, for that matter, in the Xcode GUI. I have had good success in converting some particularly poorly made cmake and Google projects to Xcode, but I still run them through in Terminal via xcodebuild. And I have my own wrapper build system to manage it all.

Oh, Apologies.

I'll start with the easy one - CPP is another form of nomeclature for referring to C++ 😉.


I'll have another go at explaining the current issues. Hopefully it comes out less vague!.


I am currently working on an iOS project written in Swift that interfaces with some C++ dependencies via Obj-C and bridging headers.

These C++ depedencies exist as git submodules in the top level iOS repo, and are built by CMake into xcodeproj structures with some build targets. These targets output static libraries and the top level iOS project links against said static libraries. In order to achieve that, the top level app target needs to know all of the static libraries it links against, but these aren't available to be linked against nor compiled until during build time.

The C++ repos are still in active development, and so I've been told I need to build from source always - and as such I can't check in the CMake'd xcodeproject and files.


The current setup has a pre-build run script phase that runs that aforementioned CMake and then ideally xcodebuild is just like, oh yeah, I knew that this broken project wasn't actually broken before it starts the actual compilation of sourcecode.


The current developer experience is.. painful.. trying to hide away the complexity of having C++ dependencies (which TBH most iOS devs - especially pure Swift ones - don't know much if anything about). You build once, get a red build, then rebuild again, and get greens build from then on. This is a work in progress, and is already much nicer/tidier than what it was..


I'm curious into your build wrapper, specifically what its inputs are, what it outputs, and how you'd go about your developer workflow from a fresh clone and also a branch switch.


Hope this makes sense!

OK. I get it. I saw CPP and thought C pre-processor.


I think one problem you have is that you absolutely have to have those static libraries available to initially construct the project. They can be deleted and re-built at any time, but you'll need them at first. They can be stubs, if necessary.


You should be able to do all of this with either aggregate targets and/or makefile-based targets. As long as the static libraries are specified somewhere in the build flow and there is some rule to build them, it should all work fine, given a suitably complicated project.


I don't know what is special about C++ that is more complex than anything else or would require multiple build passes. As long as your build system is designed correctly, one pass is all it should take. At one point in the past, I sat down with nothing but the gnu make manual and taught myself how to create makefiles from scratch. That might be a worthwhile approach in your case. Xcode can probably do it too, but like any GUI approach, you can lose track of the complexity when it is hidden six levels deep in a GUI build tool. Makefiles are more straightforward and visible.


You probably wouldn't like my build wrapper. The primary build script is Perl and the input is an XML definition of the dependencies. I never use any fresh clones. I only download archived versions that haven't changed. I couldn't manage this project otherwise. My approach to clones and branches isn't much different than Perl and XML. The latest, bleeding edges of projects have no value to me. I'm happy to let other people spin their wheels on that stuff while I build my own software.


Edit: OK, Apple. Why is the word b r a n c h i n g not allowed in the forums? That's strange.

oh sheesh, I hadn't considered C Pre-Processor as one possible meaning of the acronym!


Well, I figured it all out! Luckily I didn't have to go down the custom makefile-based targets (at least for the solution I've settled on anyway..)


So, if you're (or anyone else) is curious as to what I settled on, be warned, I've never heard of someone doing this 😁.


The project structure evolved a little, and now there is a Swift xcodeproject embedded in the C++ module, which will CMake the C++. The advantage of this is that the top level project doesn't have to worry so much about all the various static libraries, just the more familiar <SwiftXcodeproject>.framework archive. Inside of the build target for this Swift xcodeproject is a build phase which - get this - calls both CMake and _THEN_ xcodebuild on the resulting CMake output. Nested Xcodebuild commands. delicious. This has some amount of optimization (as in, only build if an environment variable is true or it hasn't ever been built yet). but, it works, and unlike the pre-build run phase script, actually outputs to the Report Navigator instead of it's output stream being swallowed up. I have library search paths setup to point to the output directory of the CMake'd project for that Swift project, so it all works pretty tidy now.


From one John to another, thanks for your help John Daniel!

So maybe the correct answer should go to John, at least to acknoledge the hint and the effort…

sure. I don't really care either way.