27

I'm developing a C++ project which is going to be enclosed on a bigger one.

I've seen that on the bigger project (is a Qt application and it's being generated from qmake) I am able to compile a single file from the linux command line, just entering the relative path to the specific file as an argument to make.

On the other hand, I'm using CMake for my own project. When I modify some code for a compilation unit and I have to modify its header file, I have to wait a long time to compile its dependencies and then its own source file. But there are some situations in which I would prefer to check whether the source code in the *.cc file is compilable without errors.

Is there a way to generate a Makefile from CMake the way qmake does this? Switching to qmake is not an option anymore.

pacorrop
  • 547
  • 1
  • 4
  • 10
  • 1
    Try `make help` on your current CMake-generated makefile. I'm pretty sure that will list how to build just a single object file. – John Jul 08 '16 at 19:30

6 Answers6

32

You do not have to add extra custom targets to your CMake scripts, as the Makefiles generated by CMake already contain .o targets for each .cc file. E.g. if you have a source file called mySourceFile.cc, there will be a Makefile in your build directory that defines a target called <Some Path>/mySourceFile.cc.o. If you cd into your build directory, you can use grep or ack-grep to locate the Makefile that defines this target, then cd into that Makefile's directory and build it.

E.g. suppose the command ack-grep mySourceFile.cc.o prints something like:

foo/bar/Makefile
119:x/y/z/mySourceFile.o: x/y/z/mySourceFile.cc.o
123:x/y/z/mySourceFile.cc.o:
124:    # recipe for building target

Then you can build mySourceFile.cc.o by doing:

cd foo/bar && make x/y/z/mySourceFile.cc.o
Ose
  • 726
  • 7
  • 13
  • 6
    you can also specify target for cmake using --target, so for example: cmake --build . --target src/foo.cpp.o – il--ya Nov 28 '18 at 18:44
  • For me `ack-grep mySourceFile.cc.o` does absolutely nothing, but `grep -r mySourceFile.cc.o .` give me a tonne of results! – Nike Jan 11 '23 at 18:04
  • When I followed this procedure (around the time of my last comment) I was getting `make: *** No rule to make target 'CMakeFiles/level/CMakeFiles/level_obj.dir/level.o'. Stop.` Now I see that this message only occurs for files that did not successfully compile the first time. 'make CMakeFiles/level/CMakeFiles/level_obj.dir/level_rdinp.o' works fine. It seems a file needs to have been compiled successfully at least once, for this to happen? – Nike Jan 14 '23 at 18:57
  • @Nike Did CMake print any error messages when you generated your Makefiles? If they were all generated correctly, then it's not obvious why any object file target would lack a make rule. You haven't provided enough information for it to be clear what the issue is though, and SO comments aren't well-suited to troubleshooting. – Ose Jan 15 '23 at 19:57
  • CMake gave no errors. Make gave an error. `make` then works on the last `.o` file that compiled successfully during the first `make` run, but doesn't work on the `.o` file where `make` failed the first time. I agree that it's not obvious why the target would lack a make rule until being compiled with `make` at least once. I agree that SO comments are not well-suited for troubleshooting. For a case where `make` crashes half-way through a build, have you tried your procedure on the first file that didn't get compiled? – Nike Jan 15 '23 at 20:36
  • I don't think I did have that issue, but bear in mind that I wrote my answer 6 years ago, and I've barely used C++ at all over the past 3 years. I can imagine this issue occurring if there's conditional logic in your CMake files that can prevent the target from being added, or perhaps if you're working with a code generator. Have you tried il--ya's suggestion above? – Ose Jan 15 '23 at 23:29
20

CMake doesn't have a generic built-in way of doing this (it's an open issue), but if you're using the Ninja generator, you can can use a special Ninja syntax for building just the direct outputs of a given source file. For example, to compile just foo.o you would use:

ninja /path/to/foo.cpp^
nocnokneo
  • 1,857
  • 20
  • 24
  • 1
    Thanks for pointing that out! I also just noticed that it works well for out-of-source builds, as long as the path you specify is relative from where you are building to the original source file location – mystery_doctor Jun 07 '18 at 16:12
  • This seems to be the most elegant solution, but is there a particular directory from which this command is intended to be run? I did it from the `build` directory and got the following line: `log: reading configuration file: ../OpenMolcas/src/level/alfAS.f^` followed immediately after by `die: error: unable to read configuration file`. – Nike Jan 11 '23 at 19:08
  • @Nike Have you checked that a full build works (by simply running `ninja` from the build directory)? – nocnokneo Jan 11 '23 at 23:11
  • @nocnokneo I was testing something on an HPC system, for which the administrators have refused to upgrade Ninja past 1.9.0 (a 2019 version). GFortran was only supported later, and when they refused to upgrade Ninja, I unfortunately had to abandon it and move on with other innovative solutions to get past my bottleneck. I wrestled with this for about 3.5 full days, and think I might have solved the issue, but need to wait for someone with permission to run the pipelines on GitLab in order to see whether or not my commit will actually be approved. Being a weekend does not help. Fingers crossed. – Nike Jan 14 '23 at 17:30
7

Not out-of-the box. CMake does not expose those "internal" makefile rules in the main makefile.

You can do this only if you consider what kind of file structure CMake uses internally. You can e.g. for compiling a single .obj files using CMake generated makefiles call

make -f CMakeFiles/myProg.dir/build.make CMakeFiles/myProg.dir/main.cc.obj

when you have something like

cmake_minimum_required(VERSION 3.1)

project(myProg CXX)

file(WRITE "main.cc" "int main()\n{\nreturn 0;\n}") 
add_executable(myProg main.cc)
Florian
  • 39,996
  • 9
  • 133
  • 149
5

To build src/foo.cpp alone:

cmake --build . --target src/foo.cpp.o
il--ya
  • 315
  • 3
  • 7
  • minor correction, it will build `src/foo.cpp` from all targets has used this cpp – Andry Nov 29 '18 at 05:55
  • You can also pass the usual options like e.g. `VERBOSE=1` to `cmake` as you would do when building this project with `make`. – Ruslan Nov 02 '21 at 15:38
1

No, CMake does not offer built-in support to compile single files.

You have to add a target for each object file, maybe by a function iterating over all files of a directory.

usr1234567
  • 21,601
  • 16
  • 108
  • 128
  • The answer Ose provided is correct. This is not helpful. While CMake does not provide targets for each TU it well provides the information to the concrete build-systems. One has to look there. – Jan Christoph Uhde Feb 13 '19 at 09:34
  • This might be the case for Makefiles. nocnokneo provides a solution for Ninja. But in general, there is no way. nocnokneo even links the according open bug. – usr1234567 Feb 13 '19 at 10:19
  • @usr1234567 You're right to say that there's no general solution (e.g. I'd be very surprised if you can compile a single file in Visual Studio when using CMake). But the person who asked the question didn't ask for a general solution. They asked for a solution that works with make. – Ose Mar 28 '19 at 15:23
1

Others have suggested ways to find the target name (ending in .cpp.o) from the .cpp filename, but if you already know the name of a target that will trigger compilation of the .cpp file and you're using ninja this suggestion should be easier.

First build the target:

ninja TriggersCppCompilationLib

Assuming your file was changed or was not yet built, ninja will print the full target name. When you see the name come up, hit enter so it is not overwritten. Then simply copy the name from the terminal (e.g. using tmux copy mode).

davidvandebunte
  • 1,286
  • 18
  • 25