12 Make and CMake
Make
1. Introduction
- What is Make?: Make is a build automation tool that automatically compiles source code into executable programs. It reads a file named
Makefile
to determine how to build a program. - Importance in Embedded Systems: In embedded systems, resources are limited and optimized code is critical. Make allows you to manage complex build pipelines efficiently, ensuring that only changed files are recompiled.
2. Basic Syntax
- Makefile Structure: A Makefile consists mainly of "rules" that define how to derive a "target".
- Target: The output or end goal, e.g., an executable file.
- Prerequisites: Files needed to build the target. This can be source files, header files, or other targets, basically anything that, if changed, would require the target to be rebuilt.
- Command: The shell command to create the target from the prerequisites.
target: prerequisites
command
- Rules, Targets, Prerequisites: Basic blocks of a Makefile. For example:
hello: hello.c
gcc -o hello hello.c
Here, hello
is the target, hello.c
is the prerequisite, and gcc -o hello hello.c
is the command to build the target.
3. Variables
- Definition: Using
=
or:=
.
CC = gcc
- Usage: Refer to variables using
$(VAR_NAME)
.
$(CC) -o hello hello.c
4. Functions
- Commonly Used Functions: Make provides built-in functions for string manipulation, filename manipulation, etc.
$(wildcard pattern)
: Collects filenames matching a pattern.$(patsubst pattern,replacement,text)
: Pattern-based string substitution.
SOURCES := $(wildcard *.c)
OBJECTS := $(patsubst %.c,%.o,$(SOURCES))
5. Conditional Statements
ifeq
,ifndef
, etc.: Conditional statements in Make allow you to alter the build process based on variables or operating environments.ifeq (arg1, arg2)
: Executes the block ifarg1
andarg2
are equal.
ifeq ($(CC),gcc)
CFLAGS = -Wall
endif
ifndef (var)
: Executes the block ifvar
is not defined.
ifndef DEBUG
CFLAGS = -O2
endif
6. Phony Targets
.PHONY
: A phony target is not a file but a label for a set of commands. Use.PHONY
to indicate that the target doesn't correspond to a file. This is useful for targets likeclean
that don't produce an output file with the same name.
.PHONY: clean
clean:
rm -f *.o
- Why Phony Targets?: They help avoid conflicts between targets and filenames, and ensure the intended action gets executed even if there's a file with the same name as the target. Usually you should specify
.PHONY
for all targets that don't produce an output file, e.g.,all
,clean
,install
, etc.
7. Automatic Variables
- What Are They?: Automatic variables are set by Make after a rule is matched. They save you from repeating filenames or directories.
$@
: The target filename.$^
: The names of all the prerequisites, separated by spaces, and without duplicates.$<
: The name of the first prerequisite.
- Usage: They are commonly used in generic rules.
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
In this example, $@
will be substituted with the .o
file name, and $<
will be replaced with the corresponding .c
file name.
8. Common Makefile Patterns
- Template to Compile Multiple Sources: You can use wildcards and automatic variables to compile multiple source files.
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
all: $(OBJS)
- Dependency Chains: Break down the build process into smaller steps with intermediary targets.
all: program
program: main.o util.o
$(CC) -o program main.o util.o
main.o: main.c
$(CC) -c main.c
util.o: util.c
$(CC) -c util.c
9. Cross-Compilation
- What is it?: Cross-compilation is the process of compiling code for a platform different from the one you are compiling on.
- Setting Up Toolchain: Define the compiler and flags specific to the target platform.
CC = arm-none-eabi-gcc
CFLAGS = -mcpu=cortex-m3
- Cross-Compile Example: A Makefile snippet to build for ARM.
all: target
target: target.c
$(CC) $(CFLAGS) -o target target.c
10. Best Practices
- Use
.DEFAULT_GOAL
: Explicitly set the default target if it's not the first one.
.DEFAULT_GOAL := all
- Include Comments: Comment your Makefile for easier maintenance.
- Utilize
$(eval ...)
Sparingly: This function is powerful but can make the Makefile hard to follow. - Keep It Simple: Makefiles can get very complex. Always aim for readability and maintainability.
- Use
.PHONY
for Non-File Targets: As mentioned earlier, always specify.PHONY
for targets that don't produce an output file.
11. Real-World Example
The corresponding section numbers are provided in the comments. Imagine you're developing firmware for an ARM-based device and have two source files: main.c
and util.c
.
# 1. Introduction
# Simple Makefile for an ARM-based embedded system project
# 3. Variables
CC = arm-none-eabi-gcc # Compiler for ARM
CFLAGS = -Wall -mcpu=cortex-m3 # Compilation flags
SRCS = $(wildcard *.c) # 4. Functions: Use wildcard to find all .c files
OBJS = $(patsubst %.c, %.o, $(SRCS)) # 4. Functions: String replacement
# 9. Cross-Compilation
# Set the default goal to 'all'. This is our custom target
# 10. Best Practices: Setting default goal
.DEFAULT_GOAL := all
# 2. Basic Syntax & 5. Conditional Statements
# Check if DEBUG is set for additional flags
ifeq ($(DEBUG), 1)
CFLAGS += -g # Add debugging flags
endif
# 8. Common Makefile Patterns
# Compile all .c files to .o
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@ # 7. Automatic Variables
# 6. Phony Targets & 10. Best Practices
.PHONY: all clean # Indicate that 'all' and 'clean' are phony targets
# 8. Common Makefile Patterns: Dependency Chains
# 'all' depends on 'firmware.elf'
all: firmware.elf
# 'firmware.elf' depends on all object files
firmware.elf: $(OBJS)
$(CC) $(CFLAGS) $^ -o $@ # 7. Automatic Variables
# 6. Phony Targets: clean
# Remove all compiled files
clean:
rm -f *.o firmware.elf
CMake
1. Introduction
-
What is CMake?: CMake is a cross-platform build-system generator. Instead of building your project, CMake generates build files based on configurations you provide. These generated files can then be processed with build tools like Make, Ninja, or even IDE-specific build systems.
-
Relevance in Embedded Systems: Yes, CMake is widely used in embedded systems development, especially as projects scale up in complexity. The ability to manage multiple targets, cross-compilation, and custom build steps makes it a powerful tool for embedded projects.
2. Installation and Setup
- How to Install: You can typically install CMake through a package manager. For example, on Ubuntu:
sudo apt install cmake
On macOS:
brew install cmake
Or you can download it from the official website.
- Initial Setup: After installing, you create a
CMakeLists.txt
file in your project directory. This file will contain all the CMake commands and configurations for your project.
3. Basic Commands
add_executable
: Defines an executable target. It specifies the name of the executable and the source files needed.
add_executable(my_program main.cpp)
target_link_libraries
: Specifies libraries to link against a target.
target_link_libraries(my_program PRIVATE some_library)
add_library
: Defines a library target. You can specify STATIC (archive library) or SHARED (dynamic library) along with the source files.
add_library(my_library STATIC util.cpp)
find_package
: Locates a package and sets variables to use it. Useful for third-party dependencies.
find_package(Threads REQUIRED)
set
: Used to set a variable’s value, commonly used for compiler flags or options.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
4. Variables and Options
set
: As mentioned earlier, theset
command sets a variable's value. It's widely used for configuring build settings.
set(MY_VARIABLE "Some Value")
option
: Allows you to provide a boolean option that the user can toggle on or off when running CMake.
option(ENABLE_DEBUG "Enable debug build" OFF)
Users can toggle this when configuring the project: cmake -DENABLE_DEBUG=ON
.
- Cache Variables: CMake has a concept of a cache where variables are stored. You can pre-populate the cache or read from it.
set(MY_CACHED_VAR "Default Value" CACHE STRING "An example cached variable")
5. Lists and Loops
list
: Thelist
command is used for list manipulation. You can append elements, find elements, sort the list, etc.
list(APPEND MY_LIST "Element1" "Element2")
foreach
: Loops over a list of items, executing the loop body for each.
foreach(item IN LISTS MY_LIST)
message("Processing item: ${item}")
endforeach()
while
: Thewhile
command lets you execute a set of commands as long as a condition is true.
set(counter 0)
while(counter LESS 10)
math(EXPR counter "${counter} + 1")
message("Counter value: ${counter}")
endwhile()
6. Toolchain Files
- Defining Compilers and Flags: A toolchain file is a CMake script where you can set compilers and flags to be used for cross-compilation.
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
- Using a Toolchain File: You specify a toolchain file when you initially configure your CMake build.
cmake -DCMAKE_TOOLCHAIN_FILE=path/to/toolchainfile.cmake ..
- Example for Embedded Systems: Here's a minimal example tailored for an ARM Cortex-M3 processor.
# toolchain-arm-cortex-m3.cmake
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
add_definitions(-mcpu=cortex-m3)
7. Find Modules and Packages
find_package
: This command attempts to find a package configuration file that CMake provides or that you have installed manually. It sets several variables to indicate whether the package was found and provides relevant details.
find_package(OpenCV REQUIRED)
find_library
: Finds a library and stores the path as a variable. This is useful for linking against system or custom libraries.
find_library(MY_LIB mylib)
- Custom Find Modules: For packages that don’t offer CMake support, you can write a custom
Find<PackageName>.cmake
module to locate the libraries and headers, and then usefind_package
as usual.
8. Exporting and Installing
install
: Theinstall
command specifies how the built files should be packaged and where they should be placed on the system.
install(TARGETS my_program DESTINATION bin)
CPack
: This is CMake's packaging tool, allowing you to create tarballs, DEBs, RPMs, NSIS installers, and more. You configure it in yourCMakeLists.txt
:
set(CPACK_PACKAGE_NAME "MyProgram")
set(CPACK_PACKAGE_VERSION "1.0")
include(CPack)
- Exporting Targets: You can also export targets (like libraries) so that other CMake projects can easily import and use them.
install(EXPORT MyLibraryTargets
FILE MyLibraryTargets.cmake
DESTINATION lib/cmake/MyLibrary)
9. Best Practices
-
Out-of-Source Builds: Always create a separate directory for your build files (
mkdir build && cd build && cmake ..
). This keeps your source directory clean. -
Use
target_*
Commands: Prefer usingtarget_include_directories
,target_compile_definitions
, etc., over their global counterparts (include_directories
,add_definitions
, etc.) for setting properties. This makes your CMakeLists more modular and easier to manage. -
Version Check: Always specify the minimum required CMake version for better compatibility.
cmake_minimum_required(VERSION 3.10)
-
Don't Hardcode Paths: Instead of hardcoding paths, use variables and generator expressions to keep things flexible.
-
Documentation and Comments: Just like with code, documenting your
CMakeLists.txt
files helps maintainability.
10. Real-World Example
# 3. Basic Commands
cmake_minimum_required(VERSION 3.10) # 9. Best Practices: Version Check
# 4. Variables and Options
set(PROJECT_NAME "EmbeddedProject")
project(${PROJECT_NAME})
# 7. Toolchain Files: Assume this is set via -DCMAKE_TOOLCHAIN_FILE=path/to/toolchainfile.cmake
# Example content for toolchainfile.cmake:
# set(CMAKE_SYSTEM_NAME Generic)
# set(CMAKE_C_COMPILER arm-none-eabi-gcc)
# set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
# 5. Lists and Loops
list(APPEND SOURCES
"main.c"
"util.c"
)
# 3. Basic Commands
add_executable(${PROJECT_NAME} ${SOURCES})
# 7. Find Modules and Packages
find_package(SomeLibrary REQUIRED)
# 3. Basic Commands
target_link_libraries(${PROJECT_NAME} PRIVATE SomeLibrary::SomeLibrary)
# 4. Variables and Options
option(ENABLE_DEBUG "Enable Debugging" OFF)
if(ENABLE_DEBUG)
target_compile_options(${PROJECT_NAME} PRIVATE -g)
endif()
# 8. Exporting and Installing
install(TARGETS ${PROJECT_NAME} DESTINATION bin)
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
set(CPACK_PACKAGE_VERSION "1.0")
include(CPack)
# 9. Best Practices
# Documentation and Comments
# No hard-coded paths used
11. Q&A
1. Question:
What is the primary purpose of a Makefile in a C/C++ project?
Answer:
The primary purpose of a Makefile is to direct the make
tool on how to compile and link a program. It specifies the relationships among files in your program and provides commands to update them.
2. Question:
How would you specify a compiler to use in a Makefile?
Answer:
You can specify a compiler by setting the CC
variable. For instance, to use gcc
, you'd write:
CC=gcc
3. Question:
In a Makefile, what is a target, and how is it defined?
Answer:
A target is typically the name of the file that is generated by a program; it could be an executable or an object file. It's defined in the Makefile followed by a colon and then the files it depends on. Underneath the target, you define the commands to run, typically indented.
target: dependencies
commands
4. Question:
What is CMake and how does it differ from a traditional Makefile?
Answer:
CMake is a cross-platform build-system generator. Users can write simple configuration files in a non-specific language, and CMake will produce Makefiles (or other build scripts) tailored to the system. While a Makefile directly instructs the make
tool, CMake abstracts away the generation of these instructions.
5. Question:
How would you instruct CMake to use a specific compiler?
Answer:
You can specify a compiler for CMake by setting the CC
and CXX
environment variables before invoking CMake. For example:
export CC=gcc
export CXX=g++
cmake path_to_source
6. Question:
In CMake, what does the add_executable
function do?
Answer:
The add_executable
function defines an executable target built from source files. For example:
add_executable(my_program main.c utils.c)
This tells CMake to generate build rules to create an executable named "my_program" from the source files main.c
and utils.c
.
7. Question:
What's the purpose of the find_package
command in CMake?
Answer:
find_package
is used to locate external libraries and tools in a system. If the package is found, it sets certain variables, usually including include directories and library names, which can then be used in subsequent CMake commands.
8. Question:
In a Makefile, how would you define a variable to hold compiler flags?
Answer:
You can define a variable in a Makefile with an assignment. For example, to hold compiler flags, you might use:
CFLAGS=-Wall -O2
9. Question:
How do you specify a required minimum version of CMake in a CMakeLists.txt
file?
Answer:
You use the cmake_minimum_required
command. For example, to specify a minimum CMake version of 3.10, you'd write:
cmake_minimum_required(VERSION 3.10)
10. Question:
How can you conditionally include files or set flags in CMake based on the platform being targeted?
Answer:
CMake provides various variables that define the platform, like WIN32
, UNIX
, APPLE
, etc. You can use these in conditional statements. For example:
if(WIN32)
# Do something specific for Windows
elseif(APPLE)
# Do something specific for macOS
else()
# Other UNIX-like systems
endif()
11. Question:
In a Makefile, what does the special variable $@
represent?
Answer:
In a Makefile, $@
represents the name of the target of the rule.
12. Question:
How do you add include directories in CMake?
Answer:
You can use the include_directories()
function. For instance:
include_directories(/path/to/includes)
13. Question:
What is the purpose of the add_subdirectory
command in CMake?
Answer:
The add_subdirectory
command adds a sub-directory to the build. CMake will descend into the given directory and process the CMakeLists.txt
file in that directory.
14. Question:
In a Makefile, how do you define a rule that is not associated with a file?
Answer:
You can define a "phony" target using the .PHONY
directive. This tells make
that the target isn't a real file. For example:
.PHONY: clean
clean:
rm -rf *.o my_program
15. Question:
How would you specify a required library for your project in CMake?
Answer:
You can use the target_link_libraries()
function. After using find_package()
to locate the library, you'd typically do something like:
find_package(MyLibrary REQUIRED)
add_executable(my_program main.c)
target_link_libraries(my_program MyLibrary::MyLibrary)
16. Question:
How can you specify different build types, like Debug or Release, in CMake?
Answer:
You can set the CMAKE_BUILD_TYPE
variable. For example:
cmake -DCMAKE_BUILD_TYPE=Release path_to_source
17. Question:
In a Makefile, what does the special variable $^
represent?
Answer:
In a Makefile, $^
represents the names of all the prerequisites, separated by spaces, with duplicates removed.
18. Question:
How do you generate an install target using CMake?
Answer:
You can use the install()
command. For instance, to install an executable target:
add_executable(my_program main.c)
install(TARGETS my_program DESTINATION bin)
19. Question:
In a Makefile, how would you handle conditional compilation based on the value of a variable?
Answer:
You can use conditional directives like ifeq
, ifneq
, etc. For example:
DEBUG = 1
ifeq ($(DEBUG),1)
CFLAGS=-g
else
CFLAGS=-O2
endif
20. Question:
How can you group source files into a static library using CMake?
Answer:
You can use the add_library()
function with the STATIC
keyword. For instance:
add_library(my_lib STATIC source1.c source2.c)
21. Question:
In CMake, how would you propagate compile options only to consumers of a library, without affecting the library itself?
Answer:
You can use target_compile_options()
with the INTERFACE
keyword.
target_compile_options(my_lib INTERFACE -DSOME_DEFINITION)
22. Question:
In a Makefile, what's the purpose of the -
prefix before a command, like -rm -f *.o
?
Answer:
The -
prefix tells make
to continue and not exit even if the command results in an error.
23. Question:
What's the difference between CMAKE_SOURCE_DIR
and CMAKE_CURRENT_SOURCE_DIR
in CMake?
Answer:
CMAKE_SOURCE_DIR
refers to the top level source directory, while CMAKE_CURRENT_SOURCE_DIR
refers to the source directory currently being processed.
24. Question:
In a Makefile, how would you define a variable whose value is the output of a shell command?
Answer:
You can use shell function. For example:
FILES := $(shell ls *.c)
25. Question:
How do you ensure in CMake that a custom command is always executed, even if the output file already exists?
Answer:
Use the ALWAYS
argument in the add_custom_command()
.
add_custom_command(OUTPUT output.txt COMMAND some_command ALWAYS)
26. Question:
What does the add_dependencies()
command do in CMake?
Answer:
It adds custom target-level dependencies. If you specify that target A
depends on target B
, B
will be built before A
.
27. Question:
In CMake, if you wanted to conditionally compile a section of your CMakeLists.txt
based on the presence of a specific library, which command would you use?
Answer:
You would use the find_package()
command in conjunction with an if
statement.
find_package(SomeLibrary)
if(SomeLibrary_FOUND)
# Compile the conditional section
endif()
28. Question:
In a Makefile, how would you set a variable conditionally only if it hasn't been set already?
Answer:
You can use the ?=
assignment. For example:
CFLAGS ?= -O2
29. Question:
How do you set a CMake variable that's cached and available for subsequent runs of CMake?
Answer:
You can use set()
with the CACHE
option.
set(MY_VARIABLE "SomeValue" CACHE STRING "My cached variable")
30. Question:
In CMake, what's the difference between add_custom_command()
and add_custom_target()
?
Answer:
add_custom_command()
defines a new command and the circumstances under which it will be executed. add_custom_target()
creates a new target, and the command associated with it will always be executed when the target is built.
31. Question:
Given the following Makefile line:
all: my_app.c
gcc -o my_app
What's the issue here?
Answer:
The gcc
command doesn't specify the source file to compile. It should be gcc -o my_app my_app.c
.
32. Question:
Consider this CMake snippet:
add_executable(my_program main.c)
target_link_libraries(my_program PRIVATE SomeLibrary)
If SomeLibrary
wasn't defined anywhere else in the project, what would be the problem?
Answer:
CMake would throw an error because it doesn't know what SomeLibrary
is. Before using SomeLibrary
, you typically need to use find_package()
or add_library()
to define or locate it.
33. Question:
In the Makefile below, what's wrong?
CFLAGS=-I./include
my_app: main.o utils.o
Answer:
There's no rule provided to link main.o
and utils.o
to produce the my_app
target.
34. Question:
Given this CMakeLists.txt line:
set(CMAKE_CXX_FLAGS "-Wall")
What could be an unintended consequence?
Answer:
This line will override any other flags that are set elsewhere for the C++ compiler, which might not be the intended behavior. It's often safer to append flags using something like list(APPEND CMAKE_CXX_FLAGS "-Wall")
.
35. Question:
Consider the Makefile snippet:
all:
@echo "Building..."
-rm -f *.o
gcc main.c -o mainApp
What's the potential problem here?
Answer:
The -rm -f *.o
will delete all .o
files, even if they weren't generated by this Makefile. If there are other object files in the directory for different purposes, they will be unintentionally removed.
36. Question:
Review this CMake snippet:
add_library(MyLib STATIC my_source.c)
add_executable(MyApp app_source.c)
target_link_libraries(MyApp MyLib)
If app_source.c
doesn't actually use anything from my_source.c
, what's the problem?
Answer:
While not a syntax error, it's unnecessary and potentially misleading to link MyApp
with MyLib
if MyApp
doesn't use anything from MyLib
.
37. Question:
In this Makefile rule, what could go wrong?
%.o: %.c
gcc -c $< -o $@
clean:
rm *.o app
Answer:
The clean
rule does not have any dependencies, but if no .o
files or app
exist in the directory, rm
will throw an error. To suppress errors for a command in a Makefile, you can prefix it with -
, like -rm *.o app
.
38. Question:
Given this CMakeLists.txt line:
set(MY_FILES main.c util.c)
Later in the CMakeLists, there's a reference to ${MY_FILEZ}
. What's the problem?
Answer:
There's a typo. The variable is defined as MY_FILES
, but it's being referenced as ${MY_FILEZ}
. This would result in an empty value for ${MY_FILEZ}
.
39. Question:
Consider this Makefile snippet:
CC_FLAGS=-O2
all: mainApp
mainApp:
gcc $(CC_FLAGS) main.c -o mainApp
What issue might arise?
Answer:
If main.c
hasn't changed, calling make
again will still recompile mainApp
, even though there's no need. To fix this, a dependency on main.c
should be added to the mainApp
target.
40. Question:
In this CMake snippet, what could be the issue?
add_library(LibA STATIC a.c)
add_library(LibB STATIC b.c)
target_link_libraries(LibA PUBLIC LibB)
Answer:
LibA
and LibB
are both static libraries. In CMake, directly linking static libraries to other static libraries doesn't combine them. This might cause confusion or linking errors if someone expects LibA
to also contain the contents of LibB
when used.
41. Question:
Given the following Makefile:
all: prog
prog: main.c
gcc main.c -o prog -DDEBUG
If you don't want to compile with the DEBUG
flag, how can this Makefile be problematic?
Answer:
Each time you run make
, even if main.c
hasn't changed, the program will recompile due to the hard-coded -DDEBUG
flag. To avoid unnecessary recompilations, flags like this should be parameterized.
42. Question:
In this CMakeLists.txt:
option(ENABLE_DEBUG "Enable debug mode" ON)
if(ENABLE_DEBUG)
add_definitions(-DDEBUG)
endif()
What might be misleading?
Answer:
The ENABLE_DEBUG
option defaults to ON
, which might unintentionally enable debug mode for users unaware of this default setting.
43. Question:
In the Makefile below, what's the potential issue?
CFILES = $(wildcard *.c)
OFILES = $(CFILES:.c=.o)
prog: $(OFILES)
gcc -o prog $(OFILES)
Answer:
While it captures all .c
files in the current directory, any new .c
files added to the directory will automatically be included. This might unintentionally compile undesired or test files.
44. Question:
Consider this CMakeLists.txt:
set(SOURCES main.c)
add_executable(prog ${SOURCES})
install(TARGETS prog DESTINATION /usr/local/bin)
What could be a potential pitfall?
Answer:
Hardcoding the destination path as /usr/local/bin
can be problematic on systems where this isn't the standard location, or where the user doesn't have write permissions. It's often better to let the user override default installation paths.
45. Question:
Given this Makefile snippet:
all: debug release
debug:
gcc main.c -o main -DDEBUG
release:
gcc main.c -o main
What's a potential problem when switching between building debug and release?
Answer:
There's no clean step, so object files from a previous build can persist and interfere with the next build. This can lead to a "debug" build containing release object files or vice versa.
46. Question:
Given the CMake line:
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
What happens if CMAKE_C_FLAGS
already contains -Wall
?
Answer:
The -Wall
flag will be duplicated. While it doesn't hurt the compilation, it makes the flag string messy and harder to debug or read.
47. Question:
In this Makefile:
prog: helper.o
gcc main.c helper.o -o prog
helper.o: helper.c helper.h
gcc -c helper.c
What's missing?
Answer:
main.c
is not listed as a dependency for the prog
target. As a result, if changes are made to main.c
, the program won't recompile.
48. Question:
For this CMakeLists.txt snippet:
add_executable(prog main.c)
set_property(TARGET prog PROPERTY C_STANDARD 99)
What might go wrong?
Answer:
The set_property
command sets the C standard to C99. However, if the code in main.c
(or any other linked source) uses features from a newer C standard, there will be compilation errors.
49. Question:
In the Makefile:
all:
gcc -o prog src/*.c
What could be an issue?
Answer:
By using the wildcard (*
), the Makefile will compile all .c
files in the src
directory. If any unrelated or test .c
files are added, they will also be compiled, possibly causing errors or unexpected behaviors.
50. Question:
Given this CMakeLists.txt:
file(GLOB SOURCES "src/*.c")
add_executable(prog ${SOURCES})
Why might this approach be discouraged?
Answer:
Using file(GLOB ...)
to specify sources can be problematic. If a new .c
file is added to the directory, CMake won't automatically know to re-run. You would have to manually re-run CMake to recognize the new source file. This can cause confusion and lead to build issues.