A JSON compilation database is a very handy output format which is parsed and used by many development tools. Unfortunately for us Apple Developers, it is not straightforward to generate one from within Xcode, as it is (probably) not Apple's priority and therefore there is no toggle/switch/setting that can be easily enabled to get this information.
There is however a solution, thanks to Apple using Clang/LLVM as their main toolchain.
The standard way to generate this with clang would be to use the -MJ
flag and give it a file name that typically corresponds to the input file. Using this flag indirectly through Xcode is hard, given that we're not aware of all the other arguments when a compiler call is executed.
However, there is a second hidden/badly documented LLVM flag: -gen-cdb-fragment-path
- it is implemented in terms of -MJ
and has the same functionality, but it's argument in contrast is an output directory.
This allows us to collect all fragments from individual compiler executions in a central location, which greatly simplifies processing them.
OTHER_CFLAGS = $(inherited) -gen-cdb-fragment-path $(PROJECT_DIR)/CompilationDatabase
CMake has this functionality built in, represented by the variable CMAKE_EXPORT_COMPILE_COMMANDS
, however this is only implemented for Makefile and Ninja generators and it is ignored for all others.
We can reuse this flag to set the required compiler flag for the generated Xcode project, which will in turn result in generated fragments. They will still need to be collected and processed separately (manually).
if(CMAKE_GENERATOR STREQUAL "Xcode")
if(CMAKE_EXPORT_COMPILE_COMMANDS)
set(CMAKE_XCODE_ATTRIBUTE_OTHER_CFLAGS "$(inherited) -gen-cdb-fragment-path ${CMAKE_SOURCE_DIR}/CompilationDatabase")
endif()
endif()
Once a build is executed with this flag, the output directory will contain a many JSON files, corresponding for each input source file that was compiled.
Each fragment on its own isn't valid JSON, but they can easily be combined into an array of objects which represents the actual compilation database.
Please check the script below for a reference implementation for combining the fragments into a compilation database.
- CLion - Generating a Compilation Database
- Clang - Generating a Compilation Database
- How to generate a JSON Compilation Database?
- JSON Compilation Database Format Specification
- LLVM: Add a new option to emit a fragment of a compilation database for each compilation
- LLVM: Add a new option to emit a fragment of a compilation database for each compilation 2
My problem seems to be that CMAKE_XCODE_ATTRIBUTE_OTHER_CFLAGS does not propagate through to the target's C++ flags in the Xcode project, only to the C flags.
This seems to be because the generated Xcode project is missing "${inherited}" inside the C++ flags section of the target settings.
If I add it manually, then things work, but that's no use, as I need CMake to generate it properly.