1#
2# Arm SCP/MCP Software
3# Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved.
4#
5# SPDX-License-Identifier: BSD-3-Clause
6#
7
8#
9# Directories
10#
11export TOP_DIR := $(shell pwd)
12export ARCH_DIR := $(TOP_DIR)/arch
13export FWK_DIR := $(TOP_DIR)/framework
14export TOOLS_DIR := $(TOP_DIR)/tools
15export PRODUCTS_DIR := $(TOP_DIR)/product
16export MODULES_DIR := $(TOP_DIR)/module
17export DOC_DIR := $(TOP_DIR)/doc
18export MOD_TEST_DIR := $(TOP_DIR)/unit_test
19export MOD_TEST_BUILD_DIR=$(BUILD_DIR)/module/test
20export FWK_TEST_DIR=$(FWK_DIR)/test
21export FWK_TEST_BUILD_DIR=$(BUILD_DIR)/framework/test
22export PROD_TEST_DIR := $(TOP_DIR)/product/test
23export PROD_TEST_BUILD_DIR=$(BUILD_DIR)/product/test
24
25#
26# Tools
27#
28export RM := rm -rf
29export MD := mkdir -p
30export CP := cp -r
31export DOC := doxygen
32export CMAKE := cmake
33export CTEST := ctest
34export CD := cd
35export LCOV := lcov
36export GENHTML  := genhtml
37export PYTHON := python3
38
39#
40# Documentation paths
41#
42export BUILD_STRING := $(shell $(TOOLS_DIR)/build_string.py 2>/dev/null)
43
44export BUILD_DOC_DIR = $(BUILD_DIR)/doc
45export MODULE_INCLUDES := $(shell ls -d $(MODULES_DIR)/*/include/ 2>/dev/null)
46export MODULE_DOCS := $(shell ls -d $(MODULES_DIR)/*/doc/ 2>/dev/null)
47export MODULE_DOCS += $(shell ls -d $(PRODUCTS_DIR)/*/module/*/doc/ 2>/dev/null)
48
49#
50# Default options
51#
52DEFAULT_MODE := release
53DEFAULT_BUILD_PATH := $(TOP_DIR)/build
54DEFAULT_VERBOSE := n
55DEFAULT_DEBUGGER := n
56DEFAULT_ENABLE_COVERAGE := n
57DEFAULT_TOOLCHAIN := GNU
58DEFAULT_BUILD_SYSTEM := Ninja
59export CMSIS_DIR := $(TOP_DIR)/contrib/cmsis/git/CMSIS/Core
60DEFAULT_LOG_LEVEL_debug := INFO
61DEFAULT_LOG_LEVEL_release := WARN
62DEFAULT_DIRECT_BUILD := n
63
64DEFAULT_CMAKE_TOOL_LOG_LEVEL := NOTICE
65
66#
67# Top-level configuration
68#
69.DEFAULT_GOAL = all
70# MAKECMDGOALS isn't set if there's no explicit goal in the
71# command line, so set the default.
72MAKECMDGOALS ?= $(.DEFAULT_GOAL)
73
74# Mode
75MODE ?= $(DEFAULT_MODE)
76ifneq ($(filter-out debug release, $(MODE)),)
77    $(error "Invalid MODE parameter. Aborting...")
78endif
79
80# Build System
81BUILD_SYSTEM ?= $(DEFAULT_BUILD_SYSTEM)
82ifneq ($(filter-out Make Ninja, $(BUILD_SYSTEM)),)
83    $(error "Invalid BUILD_SYSTEM parameter. Aborting...")
84endif
85
86# Toolchain
87TOOLCHAIN ?= $(DEFAULT_TOOLCHAIN)
88ifneq ($(filter-out GNU ArmClang Clang, $(TOOLCHAIN)),)
89    $(error "Invalid TOOLCHAIN parameter. Aborting...")
90endif
91
92# Log level
93LOG_LEVEL ?= $(DEFAULT_LOG_LEVEL_${MODE})
94CMAKE_SCP_LOG_LEVEL_OPTION=-DSCP_LOG_LEVEL=$(LOG_LEVEL)
95
96# Build directory
97BUILD_PATH ?= $(DEFAULT_BUILD_PATH)
98ifeq ($(BUILD_PATH),)
99    $(error "Invalid BUILD_PATH parameter. Aborting...")
100endif
101export BUILD_DIR = $(abspath $(BUILD_PATH))
102
103# Verbose mode: y/n
104export V ?= $(DEFAULT_VERBOSE)
105ifneq ($(V),n)
106    CMAKE_TOOL_LOG_LEVEL := --log-level=VERBOSE
107    CMAKE_BUILD_VERBOSE_OPTION := -v
108else
109    CMAKE_TOOL_LOG_LEVEL := --log-level=$(DEFAULT_CMAKE_TOOL_LOG_LEVEL)
110    MAKEFLAGS += --silent
111    MAKEFLAGS += --no-print-directory
112endif
113
114# Build directly to build directory
115DIRECT_BUILD ?= $(DEFAULT_DIRECT_BUILD)
116export DIRECT_BUILD
117
118# Include debugger library: y/n
119DEBUGGER ?= $(DEFAULT_DEBUGGER)
120ifeq ($(DEBUGGER),y)
121    CMAKE_DEBUGGER_OPTION := -DSCP_ENABLE_DEBUGGER=ON
122endif
123
124ENABLE_COVERAGE ?= $(DEFAULT_ENABLE_COVERAGE)
125
126#
127# Products
128#
129PRODUCTS := $(sort $(shell find product -name "product.mk" -printf "%h\n" | cut -d'/' -f2-3))
130
131#
132# Deprecated Products/Platforms
133#
134DEPRECATED_PLATFORMS := none
135
136PRODUCT_INDEPENDENT_GOALS := clean help test doc fwk_test mod_test prod_test
137
138ifneq ($(filter-out $(PRODUCT_INDEPENDENT_GOALS), $(MAKECMDGOALS)),)
139    ifeq ($(PRODUCT),)
140        $(error "You must define PRODUCT. Aborting...")
141    endif
142
143    PRODUCT_DIR := $(PRODUCTS_DIR)/$(PRODUCT)
144
145    ifeq ($(wildcard $(PRODUCT_DIR)/product.mk),)
146        $(error "Missing product.mk in $(PRODUCT_DIR)")
147    endif
148
149    include $(PRODUCT_DIR)/product.mk
150
151    ifeq ($(BS_FIRMWARE_LIST),)
152        $(error "You must define BS_FIRMWARE_LIST in product.mk. Aborting...")
153    endif
154
155    # Terminate the build if the chosen platform is in the list of deprecated
156    # ones.
157    ifeq ($(filter-out $(DEPRECATED_PLATFORMS), $(PRODUCT)),)
158        $(error "$(PRODUCT) has been deprecated! Build terminated")
159    endif
160
161    FIRMWARE_TARGETS := $(addprefix firmware-, $(BS_FIRMWARE_LIST))
162
163ifeq ($(DIRECT_BUILD), n)
164    ifndef PLATFORM_VARIANT
165        PRODUCT_BUILD_DIR := $(BUILD_PATH)/$(BS_PRODUCT_NAME)/$(TOOLCHAIN)/$(MODE)
166    else
167        PRODUCT_BUILD_DIR := $(BUILD_PATH)/$(BS_PRODUCT_NAME)/platform_variant_$(PLATFORM_VARIANT)/$(TOOLCHAIN)/$(MODE)
168    endif
169else
170	PRODUCT_BUILD_DIR := $(BUILD_PATH)
171endif
172
173define msg_start
174================================================================
175Arm SCP/MCP Software build System
176Platform    : $(BS_PRODUCT_NAME)
177Mode        : $(MODE)
178Firmware(s) : $(BS_FIRMWARE_LIST)
179================================================================
180endef
181
182    $(info $(msg_start))
183endif
184
185ifeq ($(MODE),debug)
186	CMAKE_BUILD_TYPE=-DCMAKE_BUILD_TYPE=Debug
187endif
188
189CMAKE_COMMAND_OPTION := $(CMAKE_BUILD_TYPE)
190CMAKE_COMMAND_OPTION += -DSCP_TOOLCHAIN="$(TOOLCHAIN)"
191CMAKE_COMMAND_OPTION += -DSCP_LLVM_SYSROOT_CC="$(LLVM_SYSROOT_CC)"
192
193ifdef PLATFORM_VARIANT
194    CMAKE_COMMAND_OPTION += -DSCP_PLATFORM_VARIANT="$(PLATFORM_VARIANT)"
195endif
196
197ifdef CMAKE_BUILD_VERBOSE_OPTION
198    CMAKE_COMMAND_OPTION += -DCOMMAND_OUTPUT_VERBOSE=1
199endif
200
201ifeq ($(TEST_ON_TARGET),1)
202    CMAKE_COMMAND_OPTION += -DTEST_ON_TARGET=1
203endif
204
205ifeq ($(BUILD_SYSTEM), Ninja)
206    CMAKE_COMMAND_OPTION += -G $(BUILD_SYSTEM)
207endif
208
209CMAKE_COMMAND_OPTION += $(CMAKE_TOOL_LOG_LEVEL)
210CMAKE_COMMAND_OPTION += $(CMAKE_SCP_LOG_LEVEL_OPTION)
211CMAKE_COMMAND_OPTION += $(CMAKE_DEBUGGER_OPTION)
212
213#
214# Rules
215#
216.PHONY: help
217
218help:
219	@echo "Arm SCP/MCP Software build system"
220	@echo ""
221	@echo "Usage: make -f Makefile.cmake [PRODUCT=<name>] [OPTIONS] [TARGET]"
222	@echo ""
223	@echo "--------------------------------------------------------------------"
224	@echo "| Available Targets                                                |"
225	@echo "--------------------------------------------------------------------"
226	@echo "    all             Build all firmware defined by PRODUCT=<product>"
227	@echo "    clean           Remove all built products"
228	@echo "    fwk_test        Build and runs framework unit tests"
229	@echo "    mod_test        Build and runs module unit tests"
230	@echo "    help            Show this documentation"
231	@echo "    doc             Generate the documentation of this project with Doxygen"
232	@echo ""
233	@echo "--------------------------------------------------------------------"
234	@echo "| Product Selection                                                |"
235	@echo "--------------------------------------------------------------------"
236	@echo "    PRODUCT"
237	@echo "        Available products: $(PRODUCTS)"
238	@echo "        Select the target product to build. This is a required"
239	@echo "        parameter."
240	@echo ""
241	@echo "--------------------------------------------------------------------"
242	@echo "| Options                                                          |"
243	@echo "--------------------------------------------------------------------"
244	@echo "    BUILD_PATH"
245	@echo "        Value: <Full, user provided path>"
246	@echo "        Default: '$(DEFAULT_BUILD_PATH)'"
247	@echo "        Set the base directory used during the build process."
248	@echo ""
249	@echo "    MODE"
250	@echo "        Value: <debug | release>"
251	@echo "        Default: '$(DEFAULT_MODE)'"
252	@echo "        Choose between release and debug mode."
253	@echo ""
254	@echo "    O"
255	@echo "        Value: <Compiler-specific optimization level>"
256	@echo "        Default: <Compiler and MODE specific>"
257	@echo "        Set the desired level of optimization the compiler will use."
258	@echo ""
259	@echo "    V"
260	@echo "        Value: <y|n>"
261	@echo "        Default: $(DEFAULT_VERBOSE)"
262	@echo "        Enable or disable verbose mode for the build system."
263	@echo ""
264	@echo "    DEBUGGER"
265	@echo "        Value: <y|n>"
266	@echo "        Default: $(DEFAULT_DEBUGGER)"
267	@echo "        Include the debugger library."
268	@echo ""
269	@echo "    LOG_LEVEL"
270	@echo "        Value: <DEBUG|INFO|WARN|ERROR|CRIT|DISABLED>"
271	@echo "        Default: $(LOG_LEVEL)"
272	@echo "        Filter log messages less important than this level."
273	@echo ""
274	@echo "    BUILD_SYSTEM"
275	@echo "        Value: <Make|Ninja>"
276	@echo "        Default: $(DEFAULT_BUILD_SYSTEM)"
277	@echo "        Specify CMake to generate GNU Make or Ninja build system."
278	@echo ""
279	@echo "    TOOLCHAIN"
280	@echo "        Value: <GNU|ArmClang|Clang>"
281	@echo "        Default: $(DEFAULT_TOOLCHAIN)"
282	@echo "        Specify Toolchain to build the firmware."
283	@echo ""
284	@echo "    LLVM_SYSROOT_CC"
285	@echo "        Value: <LLVM sysroot compiler path>"
286	@echo "        Specify LLVM sysroot compiler path to build the firmware."
287	@echo ""
288	@echo "    PLATFORM_VARIANT"
289	@echo "        Value: <Platform variant>"
290	@echo "        Specify Platform variant if it is required."
291	@echo ""
292	@echo "    EXTRA_CONFIG_ARGS"
293	@echo "        Value: <cmake configuration parameters>"
294	@echo "        Default: "
295	@echo "        Pass extra arguments directly to cmake configuration stage."
296	@echo "        Multiplle extra args can be added with += or by passing the arguments as a string"
297	@echo ""
298	@echo "    EXTRA_BUILD_ARGS"
299	@echo "        Value: <cmake build parameters>"
300	@echo "        Default: "
301	@echo "        Pass extra arguments directly to cmake build stage."
302	@echo "        Multiplle extra args can be added with += or by passing the arguments as a string"
303	@echo ""
304	@echo "    ENABLE_COVERAGE"
305	@echo "        Value: <y|n>"
306	@echo "        Default: n"
307	@echo "        Enable or disable generation of code coverage reports for unit tests."
308	@echo "        Use ENABLE_COVERAGE=y to enable coverage for 'fwk_test' and 'mod_test' targets."
309	@echo ""
310	@echo "    DIRECT_BUILD"
311	@echo "        Value: <y|n>"
312	@echo "        Default: n"
313	@echo "        Build directly to the specified build path without modifying it."
314	@echo ""
315
316.SECONDEXPANSION:
317
318.PHONY: all
319all: $(FIRMWARE_TARGETS)
320
321firmware-%: $(PRODUCT_BUILD_DIR)/$$@/CMakeCache.txt
322	$(CMAKE) --build $(<D)/ $(CMAKE_BUILD_VERBOSE_OPTION) $(EXTRA_BUILD_ARGS)
323
324.PRECIOUS: $(PRODUCT_BUILD_DIR)/firmware-%/CMakeCache.txt
325
326$(PRODUCT_BUILD_DIR)/firmware-%/CMakeCache.txt:  $(PRODUCT_DIR)/%/Firmware.cmake
327	$(RM) $(@D)
328	$(CMAKE) -B $(@D) -DSCP_FIRMWARE_SOURCE_DIR:PATH=$(PRODUCT_DIR)/$* $(CMAKE_COMMAND_OPTION) $(EXTRA_CONFIG_ARGS)
329
330.PHONY: clean
331clean:
332	$(RM) $(BUILD_DIR)
333
334
335.PHONY: doc
336doc:
337	mkdir -p $(BUILD_DOC_DIR)
338	$(DOC) $(DOC_DIR)/Doxyfile
339
340.PHONY: test
341test : fwk_test mod_test
342
343.PHONY: fwk_test
344fwk_test:
345	$(CMAKE) -B $(FWK_TEST_BUILD_DIR) $(FWK_TEST_DIR) -G Ninja
346	$(CMAKE) --build ${BUILD_PATH}/framework/test
347	# --test-dir option of ctest is not available before ctest 3.20
348	# so use workaround to change the test dir and run the tests from there
349	${CD} $(FWK_TEST_BUILD_DIR) && ${CTEST} -V --output-junit Testing/TestResults.xml
350ifeq ($(ENABLE_COVERAGE),y)
351	${CD} $(FWK_TEST_BUILD_DIR) && $(LCOV) --capture --directory . --output-file scp_v2_fwk_test_coverage.info
352	${CD} $(FWK_TEST_BUILD_DIR) && $(PYTHON) $(TOOLS_DIR)/filter_coverage_report.py --filename scp_v2_fwk_test_coverage.info
353	${CD} $(FWK_TEST_BUILD_DIR) && $(GENHTML) scp_v2_fwk_test_coverage_filtered.info --prefix "$(TOP_DIR)" --output-directory $(FWK_TEST_BUILD_DIR)/coverage_report
354endif
355
356.PHONY: mod_test
357mod_test:
358	$(CMAKE) -B $(MOD_TEST_BUILD_DIR) $(MOD_TEST_DIR) -G Ninja
359	$(CMAKE) --build $(MOD_TEST_BUILD_DIR)
360	${CD} $(MOD_TEST_BUILD_DIR) && $(CTEST) -V --output-junit Testing/TestResults.xml
361ifeq ($(ENABLE_COVERAGE),y)
362	${CD} $(MOD_TEST_BUILD_DIR) && $(LCOV) --capture --directory $(MOD_TEST_BUILD_DIR) --output-file scp_v2_unit_test_coverage.info
363	${CD} $(MOD_TEST_BUILD_DIR) && $(PYTHON) $(TOOLS_DIR)/filter_coverage_report.py --filename scp_v2_unit_test_coverage.info
364	${CD} $(MOD_TEST_BUILD_DIR) && $(GENHTML) scp_v2_unit_test_coverage_filtered.info --prefix "$(TOP_DIR)" --output-directory $(MOD_TEST_BUILD_DIR)/coverage_report
365endif
366
367.PHONY: prod_test
368prod_test:
369	$(CMAKE) -B $(PROD_TEST_BUILD_DIR) $(PROD_TEST_DIR) -G Ninja
370	$(CMAKE) --build $(PROD_TEST_BUILD_DIR)
371	${CD} $(PROD_TEST_BUILD_DIR) && $(CTEST) -V --output-junit Testing/TestResults.xml
372ifeq ($(ENABLE_COVERAGE),y)
373	${CD} $(PROD_TEST_BUILD_DIR) && $(LCOV) --capture --directory $(PROD_TEST_BUILD_DIR) --output-file scp_v2_product_test_coverage.info
374	${CD} $(PROD_TEST_BUILD_DIR) && $(PYTHON) $(TOOLS_DIR)/filter_coverage_report.py --filename scp_v2_product_test_coverage.info
375	${CD} $(PROD_TEST_BUILD_DIR) && $(GENHTML) scp_v2_product_test_coverage_filtered.info --prefix "$(TOP_DIR)" --output-directory $(PROD_TEST_BUILD_DIR)/coverage_report
376endif
377