How to get Xcode to automatically build and package a driver

I have a workspace containing a DriverKit USB driver and the containing application that utilizes the driver. These are in separate projects.

How do I configure the application project or the workspace so that, when I build the app, the driver is automatically built and copied into the application package as required for delivery? Currently I have to manually select the driver target, build, and then select the app target and build. In order to get the driver into the app, I have included it as a build-phase dependency but I had to hardcode the driver project output path and it doesn't build it automatically.

Additionally, because I hard-coded the debug path, when I try and create an Archive to deliver the app, the app includes the debug version of the driver instead of the release version.

I'm obviously doing this wrong because it is as clunky as can be.

Answered by ssmith_c in 893164022

The way this usually works is that you create a new dext target in your app project. Xcode then takes care of the build dependencies for you.

In some senses, the easiest way to go forward is for you to make a new dext target in your existing app's project, then move your code files into that target.

If you really need to maintain two separate projects, then I think you'll need to fall back on scripting to build the right targets.

Accepted Answer

The way this usually works is that you create a new dext target in your app project. Xcode then takes care of the build dependencies for you.

In some senses, the easiest way to go forward is for you to make a new dext target in your existing app's project, then move your code files into that target.

If you really need to maintain two separate projects, then I think you'll need to fall back on scripting to build the right targets.

How do I configure the application project or the workspace so that, when I build the app, the driver is automatically built and copied into the application package as required for delivery?

So, I'd actually recommend against routinely rebuilding your DEXT like this. Driver development isn't the same as app development, in that most drivers tend to reach a point where they're "done", at which point routine changes are far less common. Over time you may fix bugs or improve the driver, but that tends to be a relatively slow process compared to typical app development.

Because of that, all routine rebuilds do is inject noise and randomness into what would otherwise be a stable resource. Even worse, they create a new source of uncertainty that would otherwise not exist— that is, if your driver’s version changes with every release/build, then you need to validate that any problem was caused by an issue with the driver, even though the driver itself didn't change.

Note that this isn't an abstract concern. There has been at least one system update where an SDK issue meant that DEXT built with that SDK would ONLY load on THAT particular system version. Developers who shipped a "static" DEXT at least had a product that worked on every version EXCEPT that release, which is clearly better than a version that only works on a single minor system update.

That would generally be my advice even if DriverKit codesigning wasn't problematic, but, of course, DriverKit codesigning is somewhat problematic, which is the other point behind my recommendation.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

@Kevin Elliott

So, are you recommending that I build a debug and release version of the DEXT and make them an include dependency of the debug and release build process for the app that relies on the DEXT, sort of like I'm doing with the three key XCFrameworks that are also being included in the final application?

If so, I would only rebuild and release the driver when it actually needed updating, yes? That would mean I could make the DEXT a part of the SDK workspace that builds the three XCFrameworks and have all of the build products dropped into a common build-output directory. Currently, I'm taking those frameworks and explicitly updating my application project file to include them, every time I have a new SDK release.

If I understand you correctly, this might actually be the better approach for my case.

So, are you recommending that I build a debug and release version of the DEXT and make them an include dependency of the debug and release build process for the app that relies on the DEXT, sort of like I'm doing with the three key XCFrameworks that are also being included in the final application?

So, the debug build is somewhat more complicated, as debug builds expire relatively frequently and only work on machines that are embedded in their signing profile. That means you'll end up needing to rebuild more frequently, either because the build expired or you got new hardware you need to test on. There's a judgement call to be made there between the options of:

  • Relying entirely on automatic signing and accepting that you'll technically be running on a slightly different DEXT version.

  • Using command line codesigning to resign a static build.

  • Living with the periodic need to "refresh" your development build as needed.

However, in terms of the release build:

If so, I would only rebuild and release the driver when it actually needed updating, yes?

Yes, that is absolutely what I suggested. It is not unusual for a driver version to work for years without modification, so there isn't a lot of value in continually rebuilding it.

That would mean I could make the DEXT a part of the SDK workspace that builds the three XCFrameworks and have all of the build products dropped into a common build-output directory. Currently, I'm taking those frameworks and explicitly updating my application project file to include them, every time I have a new SDK release.

Ehh... I could argue either side on whether or not DEXTs should be rebuilt for SDK and system updates. I THINK I'd lean toward just shipping the same build "forever", but I could also see value in specifically rebuilding and testing on each SDK release, even if you didn't actually ship that build. That would help keep you informed about deprecations or other changes, so you don't accidentally end up with a large work backlog simply because you haven't touched the driver in 2 years.

Note that this is a VERY different than frameworks or libraries. The issue with framework/libraries is that our APIs actually change behavior based on the SDK build information embedded in the library, so mixing SDK versions across app components can create weird edge cases that would otherwise not exist. The worse case here is actually static libraries, as those end up directly embedded in the executable, so they "inherit" the SDK version of the object they're embedded in, even if they were compiled on a totally different SDK.

As a concrete example of this, if you embed a very old static library into a modern executable, basic types like NSNumber can become quite broken. Several years ago we introduced a new format that encodes the NSNumber value into its "pointer" (so there isn't actually a true object being allocated). Overall that transition went quite smoothly because the SDK version system meant that we only used that new format in executables that we "knew" would understand that format. However, if a static library means a chunk of your app which doesn't "know" about that new format receives that new format, then you'll pretty quickly crash in somewhat odd ways.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

How to get Xcode to automatically build and package a driver
 
 
Q