972
edits
[unchecked revision] | [unchecked revision] |
(Added some stuff to try and follow the style of the other build tutorials.) |
mNo edit summary Tag: Manual revert |
||
(9 intermediate revisions by 6 users not shown) | |||
Line 1:
{{Rating|1}}
CMake is a cross-platform, multi-environment build system which can
== About CMake ==
CMake is
=== Who uses CMake? ===
Because CMake was developed by KitWare, it is closely associated with their software. However, momentum for CMake has steadily been increasing, and some fairly high-profile projects have switched over to it, including:
Line 16:
* ReactOS
== Getting Started ==▼
=== Design Considerations ===▼
▲=Getting Started=
▲==Design Considerations==
There are a variety of options at your disposal when you design your CMake project. It is possible to have CMake cater to some of your personal build preferences, and as a result, you should probably ask yourself a few questions:
Line 28 ⟶ 27:
* Will I be generating any code from templates?
The first question is largely one of preference. An ''in-source'' build means that the build output will be placed in the same directory
<pre>
Line 51 ⟶ 50:
</pre>
The above
*
* ISA-dependent code can be isolated from common code or other ISA-dependent code.
* Platform-dependent code (which may add further restrictions to ISA-dependent code) is separated from other platforms sharing the same ISA.
The chances are that if you're building an operating system, you won't have many library dependencies. Most of your dependencies will be related to the toolchain required to build the code. For instance, you will probably want a C compiler, and almost certainly an assembler to go with it. You may write your own tools to simplify the process of creating your operating system, and in that case, you may want to ensure support for other programming environments like Python or Perl. Fortunately, this part can be fairly forgiving: adding dependencies such as these is not terribly difficult, and if you change your mind, the change is easy to implement.
=== A Simple CMakeLists.txt ===
One of the nice things about CMake is that it affords you several programming concepts that you are already familiar with. As one would expect, CMake lets you perform build configuration through variables, which are typically defined in user-provided files named CMakeLists.txt. The resulting values of these variables, after cmake processing, can conveniently be found in a file called CMakeCache.txt.
Of course, variables aren't really enough, so CMake provides two kinds of procedures: macros and functions. Functions and macros are very subtly different: functions create their own environment
A useable CMakeLists.txt can have as little as two lines as code. One of the most important families of macros include <code>ADD_EXECUTABLE()</code> or <code>ADD_LIBRARY()</code>.
Each of these macros take several parameters: the target name as the first parameter and the source file(s) on which the target depends on as the remaining parameter(s).
Each of these each take several parameters, which includes the target name (1st param) and the source files. (remaining params) The CMAKE_MINIMUM_REQUIRED function is also required, because that is how CMake can tell whether or not it meets the requirements to parse your script. However, such a script does have its disadvantages: every time we add a new source file, we need to edit CMakeLists.txt. Fortunately, CMake comes with a built in file manipulation interface. In this case, we might want to use the GLOB operator for the FILE() command, which lets us specify a file globbing pattern to collect all of our source filepaths into one variable. The addition of this extra line of code can make CMake much more useful.▼
The <code>CMAKE_MINIMUM_REQUIRED()</code> function, which takes the version of CMake necessary to parse the CMakeList.txt file is also required, because that is how CMake can tell whether or not it meets the requirements to parse your script.
If you have dependencies you need to resolve, CMake can handle that too. The FIND_PACKAGE command can handle a variety of different dependencies, including libraries (like Boost). FIND_PACKAGE will attempt to locate a script by the name of Find''<1st param>''.cmake and handle it appropriately. In instances where FIND_PACKAGE is unable to resolve the package, it sets a special variable called ''<1st param>''_NOTFOUND, which you might use to detect optional dependencies. However, if a dependency is absolutely required, then you can simply supply REQUIRED as the second parameter. Failing to find the package will cause CMake to bail out.▼
▲
Finally, you can emit messages back to the console using the MESSAGE command. Note that this is only run at CMake time. You can use this command for debugging your CMake project. Alternately, if you supply STATUS as the first parameter to this command, it will print out a specialized status message for you.▼
▲If you have dependencies you need to resolve, CMake can handle that too. The <code>FIND_PACKAGE()</code> command can handle a variety of different types of dependencies, including libraries (like Boost). <code>FIND_PACKAGE()</code> will also attempt to locate a script by the name of <code>Find''<1st param>''.cmake</code> and handle it appropriately. In instances where <code>FIND_PACKAGE()</code> is unable to resolve the package, it sets a special variable called <code>''<1st param>''_NOTFOUND</code>, which you might use to detect optional dependencies. However, if a dependency is absolutely required, then you can simply supply <code>REQUIRED</code> as the second parameter. Failing to find the package will cause CMake to bail out.
▲
<syntaxhighlight lang="cmake">
# So CMake can tell whether or not it can process this file
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.0)
Line 86 ⟶ 89:
# Note how we obtain this variable's value!
ADD_EXECUTABLE(foo ${C_SRCS})
</syntaxhighlight>
This is enough for a small project to generate an executable. Creating the associated Makefile and starting the build is simple:
<pre>
$ cd project/
$ cmake .▼
$ mkdir build
$ cd build/
▲$ cmake ../
$ make
</pre>
Line 97 ⟶ 103:
Note that if you chance CMakeLists.txt, you will need to run CMake again. In cases like these, CMake may need to update CMakeCache.txt, for instance. Particularly in the case where you use file globbing find your source files, it is imperative that you do this when you add or delete source files; otherwise, CMake will not pick up the changes, and havoc will break loose.
==
The previous section discussed a sort of "Hello World" implementation of CMakeLists.txt. Unfortunately, operating system development is rarely in the same class as "Hello World", and the chances are that you have advanced beyond intro
<syntaxhighlight lang="cmake">
ADD_SUBDIRECTORY(src/kernel)
ADD_SUBDIRECTORY(src/libc)
</syntaxhighlight>
Using the <code>ADD_SUBDIRECTORY()</code> command is analagous to recursively calling make, and in many cases, this is precisely what happens. Other generators may interpret this command
Alternately, you can leverage the <code>INCLUDE()</code> command to directly insert CMake code into your CMakeLists.txt file at its point of invocation, which can be useful for important small snippets of code into your project. Note that there are some subtle differences between <code>INCLUDE()</code> and <code>ADD_SUBDIRECTORY()</code>:
* You can use <code>INCLUDE()</code> to include any file as CMake code. <code>ADD_SUBDIRECTORY()</code> expects a CMakeLists.txt file in the directory you point it at.
* <code>INCLUDE()</code> operates from the current working directory. <code>ADD_SUBDIRECTORY()</code> will change the current working directory to the supplied path
== Applying CMake to your Operating System ==
=== Building Assembly Code ===
Unless you intend to use somebody else's kernel and write your operating system completely from portable code, it is very likely that you will
<syntaxhighlight lang="cmake">
# We want an AT&T Syntax Assembler
ENABLE_LANGUAGE(ASM-ATT)
ADD_EXECUTABLE(foo bar.s baz.s)
</syntaxhighlight>
=== Setting Appropriate Build Options ===
Depending on your project
* For C programs, you can use <code>CMAKE_C_FLAGS</code> in the same way you would use <code>$CFLAGS</code> in the context of make. <code>ADD_DEFINITIONS()</code> can also be used, but it is probably inadvisable to do so since a C flag variable exists by default.
* For other languages, (including assembly) use <code>CMAKE_''<lang>''_COMPILE_OBJECT</code>. For instance, if ASM-ATT is enabled, one would modify <code>CMAKE_ASM-ATT_COMPILE_OJBECT</code>.
* Link-time options can be set using <code>SET_TARGET_FLAGS(''target'' PROPERTIES LINK_FLAGS "''flags''")</code>.
=== Build Profile Detection ===
Remember how we made the claim that the directory structure could be leveraged in order to help make our lives easier? You might have gotten some idea from the directory structure of how this might be accomplished, at least on a conceptual level. Consider the example provided by ''src/kernel''. Here, we have a well-defined
* <code>/src/kernel/</code> contains the code for the kernel. (platform-indepdendent code might go into this directory).
* <code>/src/kernel/
* <code>/src/kernel/
Let's consider the ARM branch of our code. While the i386 may see limited usage outside of IBM-compatible PCs, the ARM conversely is found in a number of environments. Many of those environments have their own quirks, such as how the memory bus is physically mapped. Will the kernel binary for a BeagleBone run on a Raspberry Pi? It
We can take two approaches to this problem: we can either make the assumption platforms (and ISAs for that matter) share some things in common, or make the assumption that they don't. In the former approach, we might assume that the following is true of platforms:
Line 154 ⟶ 160:
And likewise, we might say that each ISA provides:
*
*
So far, so good. We can approach this by writing a function in order to handle loading the right profile. When you write a function, you delimit the function body between the <code>FUNCTION()</code> and <code>ENDFUNCTION()</code> commands. The first parameter to <code>FUNCTION()</code> shall be the name of the function you are defining, and those remaining are formal parameters to the function.
However, there is a slight problem: we need a way to report our findings to the caller. Remember that variables defined in functions go out of scope as soon as the function ends. We could use a macro here
<syntaxhighlight lang="cmake">
FUNCTION(LOAD_PROFILE ISA PLATFORM)
# Obtain sources for the ISA
Line 172 ⟶ 178:
# Now export our output variables
SET(PLATFORM_LAYOUT "${ISA}/${PLATFORM}/layout.ld)▼
SET(ISA_SRCS ${ISA_SRCS} PARENT_SCOPE)
SET(PLATFORM_SRCS ${PLATFORM_SRCS} PARENT_SCOPE)
▲ SET(PLATFORM_LAYOUT "${ISA}/${PLATFORM}/layout.ld" PARENT_SCOPE)
# And specific flags
Line 180 ⟶ 186:
SET(ISA_ASM_FLAGS ${ISA_ASM_FLAGS} PARENT_SCOPE)
# ...
</syntaxhighlight>
Now, all we have to do is call <code>LOAD_PROFILE()</code> with the provided parameters, and we should be able to set up our build environment in a sane manner:
<syntaxhighlight lang="cmake">
FILE(GLOB GENERIC_SRCS "*.c")
Line 196 ⟶ 202:
SET(CMAKE_ASM-ATT_COMPILE_OBJECT
"<CMAKE_ASM-ATT_COMPILER> ${
SET(CMAKE_C_FLAGS "${ISA_C_FLAGS} ${PLATFORM_C_FLAGS}")
SET_TARGET_PROPERTIES(kernel PROPERTIES LINK_FLAGS
"-T ${PLATFORM_LAYOUT} -N ${ISA_LINKER_FLAGS} ${PLATFORM_LINKER_FLAGS}")
</syntaxhighlight>
Here, we make a reasonable attempt to control the build order, but the truth is, we don't really know exactly what that order should be; it might
be dependent on the platform. For instance, for i386/pc, we might want a multiboot header, which must come within the first 8K of the kernel image. In that case, we must somehow control the ordering. We could have a place <code>FIRST_SRCS()</code> variable present in the platform flags, then use the following loop to extract it from the list:
<syntaxhighlight lang="cmake">
FOREACH(I ${FIRST_SRCS})
# Assume path is relative to src/kernel
LIST(APPEND TMP_FIRST_SRCS "${CMAKE_CURRENT_LIST_DIR}/${I}"
ENDFOREACH(I)
Line 217 ⟶ 223:
# During exports:
SET(FIRST_SRCS ${TMP_FIRST_SRCS})
</syntaxhighlight>
Now, all we have to do is put <code>${FIRST_SRCS}</code> at the head of the list, and we can control the order in which our code is linked.
== See Also ==
=== Articles ===
* [[Makefile]] - One potential target for CMake. The tried and true method of build management.
* [http://www.cmake.org CMake Offical Page] - Contains useful links, including how to download and documentation▼
=== External Links ===
▲* [http://www.cmake.org CMake Offical Page] - Contains useful links, including how to download and documentation.
* [http://www.cmake.org/Wiki/CMake_Useful_Variables CMake Useful Variables] - Gives names and descriptions of common and useful variables used in CMake.
* [http://www.cmake.org/cmake/help/v3.2 CMake Official Documentation] - Official documentation all about CMake.
* [http://www.cmake.org/Wiki/CMake/Examples CMake Examples] - Examples showing how to perform many common CMake procedures.
[[Category:Build Tools]]
[[Category:Tools]]
|