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
Hey @nguyenvukhang,
Thanks for your feedback. I just tested it on macOS 13.3.1 with Xcode 14.3 (14E222b) and it was working as expected.
The result that you're describing can happen if e.g. you have a pure, Swift only target, meaning no c/cpp/objc files as part of that target.
As the functionality in the script is passed along using
OTHER_CFLAGS
, no compilation database will be generated if clang wasn't ever executed.