# -*- makefile -*- # vim: set filetype=make : # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # Embedded Controller firmware build system - common targets # FLATSIZE_FILE ?= .sizes.txt BUILD_DIR := $(firstword $(subst /, ,$(out))) build-utils := $(foreach u,$(build-util-bin),$(out)/util/$(u)) host-utils := $(foreach u,$(host-util-bin),$(out)/util/$(u)) build-srcs := $(foreach u,$(build-util-bin),$(sort $($(u)-objs:%.o=util/%.c) util/$(u).c)) host-srcs := $(foreach u,$(host-util-bin),$(sort $($(u)-objs:%.o=util/%.c) util/$(u).c)) # Don't do a build test on the following boards: skip_boards = OWNERS host it83xx_evb boards := $(filter-out $(skip_boards),$(notdir $(wildcard board/* private*/board/*))) # Create output directories if necessary _common_dir_create := $(foreach d,$(common_dirs),$(shell [ -d $(out)/$(d) ] || \ mkdir -p $(out)/$(d))) _sharedlib_dir_create := $(foreach d,$(dirs),$(shell \ [ -d $(out)/$(SHOBJLIB)/$(d) ] || mkdir -p $(out)/$(SHOBJLIB)/$(d))) _dir_create := $(foreach d,$(dirs),$(shell [ -d $(out)/$(BLD)/$(d) ] || \ mkdir -p $(out)/RO/$(d); mkdir -p $(out)/RW/$(d))) _dir_y_create := $(foreach d,$(dirs-y),$(shell [ -d $(out)/$(BLD)/$(d) ] || \ mkdir -p $(out)/RO/$(d); mkdir -p $(out)/RW/$(d))) # Decrease verbosity unless you pass V=1 quiet = $(if $(V),,@echo ' $(2)' $(subst $(out)/,,$@) ; )$(cmd_$(1)) silent = $(if $(V),,1>/dev/null) silent_err = $(if $(V),,2>/dev/null) # commands to build all targets cmd_lds = $(CPP) -P -C -MMD -MF $@.d -MT $@ $(CPPFLAGS) $< -o $@ cmd_lds_b = $(cmd_lds) -DRW_B_LDS # Allow obj_to_bin to be overridden by board or chip specific commands cmd_obj_to_bin ?= $(OBJCOPY) --gap-fill=0xff -O binary $^ $(out)/$*.bin.tmp cmd_flat_to_obj = $(CC) -T $(out)/firmware_image.lds -nostdlib $(CPPFLAGS) \ -Wl,--build-id=none -o $@ $< # Allow the .roshared section to overlap other sections (itself) cmd_ec_elf_to_flat ?= $(OBJCOPY) --set-section-flags .roshared=share \ -O binary $< $@ cmd_elf_to_signed ?= sudo $(SIGNER) --key=util/signer/$(3) \ --input=$< --format=bin --output=$@.signed $(SIGNER_EXTRAS) \ && sudo chown $(shell whoami) $@.signed && mv $@.signed $@ cmd_elf_to_dis = $(OBJDUMP) -D $< > $@ cmd_elf_to_hex = $(OBJCOPY) -O ihex $< $@ cmd_bin_to_hex = $(OBJCOPY) -I binary -O ihex \ --change-addresses $(_program_memory_base) $^ $@ cmd_smap = $(NM) $< | sort > $@ cmd_elf = $(CC) $(objs) $(libsharedobjs_elf-y) $(LDFLAGS) \ -o $@ -Wl,-T,$< -Wl,-Map,$(patsubst %.elf,%.map,$@) cmd_exe = $(CC) $(ro-objs) $(HOST_TEST_LDFLAGS) -o $@ cmd_c_to_o = $(CC) $(CFLAGS) -MMD -MF $@.d -c $< -o $(@D)/$(@F) cmd_c_to_build = $(BUILDCC) $(BUILD_CFLAGS) \ $(sort $(foreach c,$($(*F)-objs),util/$(c:%.o=%.c)) $*.c) \ $(BUILD_LDFLAGS) \ -MMD -MF $@.d -o $@ cmd_c_to_host = $(HOSTCC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -MMD -MF $@.d -o $@ \ $(sort $(foreach c,$($(*F)-objs),util/$(c:%.o=%.c)) $*.c) cmd_cxx_to_host = $(HOSTCXX) -std=c++0x $(COMMON_WARN) $(HOST_CXXFLAGS)\ -I ./$($(notdir $@)_ROOT) -o $@ $(filter %.cc,$^) $($(notdir $@)_LIBS) cmd_host_test = ./util/run_host_test $* $(silent) cmd_version = ./util/getversion.sh > $@ cmd_mv_from_tmp = mv $(out)/$*.bin.tmp $(out)/$*.bin cmd_extractrw-y = dd if=$(out)/$(PROJECT).bin.tmp of=$(out)/$(PROJECT).RW.bin \ bs=1 count=$(_rw_size) skip=$(_rw_off) $(silent_err) cmd_copyrw-y = cd $(out) && cp RW/$(PROJECT).RW.flat RW/$(PROJECT).RW.bin cmd_sharedlib_elf = $(CC) $(libsharedobjs_deps) \ -Wl,-T,common/ec.$(SHOBJLIB).ld $(LDFLAGS) \ -o $(out)/$(SHOBJLIB)/$(SHOBJLIB).elf \ -Wl,-Map,$(out)/$(SHOBJLIB)/$(SHOBJLIB).map # commands for RSA signature cmd_rsasign = futility sign --type usbpd1 --pem $(PEM) $(out)/$*.bin.tmp # commands to build optional xref files cmd_deps_to_list = cat $(deps) | tr -d ':\\' | tr ' ' '\012' \ | egrep '\.[chS]$$' | sort | uniq > $@ cmd_etags = etags -o $@ $(shell cat $<) cmd_ctags = ctags -o $@ $(shell cat $<) targ_if_prog = $(if $(shell which $(1) 2>/dev/null),$(2),) # By default, the "build_boards" and "try_build_boards" targets will build all # of the boards listed in $(boards). However, the invoker can provide a # different list via the BOARDS variable. Providing an empty value for BOARDS # is not allowed. BOARDS ?= $(boards) ifeq ($(BOARDS),) $(error BOARDS must be non-empty) endif FAILED_BOARDS_DIR = .failedboards # When building with -j, it's easy to miss errors. If you don't have your shell # configured to warn you about nonzero exit, you may not even notice that "make # buildall -j" failed. To make it more obvious, we'll do one level of recursion # here. .PHONY: try_build_boards try_build_boards: $(foreach b, $(BOARDS), proj-$(b)) .PHONY: build_boards build_boards: @rm -rf $(FAILED_BOARDS_DIR) @mkdir $(FAILED_BOARDS_DIR) @for b in $(BOARDS); do echo 'starting' > $(FAILED_BOARDS_DIR)/$$b; done $(MAKE) try_build_boards .PHONY: buildall buildall: build_boards $(MAKE) build_cts $(MAKE) runtests @touch .tests-passed @echo "$@ completed successfully!" showboards: @echo $(sort $(boards)) # Print any important notices at the end of the build. .PHONY: notice notice: $(config) ifeq ($(CONFIG_EXPERIMENTAL_CONSOLE),y) ifeq ($(TEST_BUILD),) @echo "*** NOTE: The experimental console is ENABLED. ***" @echo "You will need to run the EC-3PO interactive console in the util" @echo "directory! Otherwise, you won't be able to enter any commands." endif # not a TEST_BUILD endif # CONFIG_EXPERIMENTAL_CONSOLE=y proj-%: @echo 'building' > $(FAILED_BOARDS_DIR)/$* @echo "======= building $*" $(MAKE) --no-print-directory BOARD=$* V=$(V) @rm $(FAILED_BOARDS_DIR)/$* dis-y := $(out)/RW/$(PROJECT).RW.dis dis-$(CONFIG_FW_INCLUDE_RO) += $(out)/RO/$(PROJECT).RO.dis dis-$(CONFIG_SHAREDLIB) += $(out)/$(SHOBJLIB)/$(SHOBJLIB).dis dis: $(dis-y) .PHONY: dis hex-y := $(out)/RO/$(PROJECT).RO.hex $(out)/RW/$(PROJECT).RW.hex $(out)/$(PROJECT).hex hex: $(hex-y) .PHONY: hex .PHONY: utils-host utils-host: $(host-utils) .PHONY: utils-build utils-build: $(build-utils) .PHONY: utils utils: utils-host utils-build # On board test binaries test-targets=$(foreach t,$(test-list-y),test-$(t)) .PHONY: $(test-targets) ifeq "$(CONFIG_COMMON_RUNTIME)" "y" $(test-targets): test-%: @set -e ; \ echo " BUILD $(out)/$*" ; \ $(MAKE) --no-print-directory BOARD=$(BOARD) PROJECT=$* \ V=$(V) out=$(out)/$* TEST_BUILD=y; \ cp $(out)/$*/$*.bin $(out)/test-$*.bin endif .PHONY: tests tests: $(test-targets) # Emulator test executables host-test-targets=$(foreach t,$(test-list-host),host-$(t)) run-test-targets=$(foreach t,$(test-list-host),run-$(t)) .PHONY: $(host-test-targets) $(run-test-targets) $(host-test-targets): host-%: @set -e ; \ echo " BUILD host - build/host/$*" ; \ $(MAKE) --no-print-directory BOARD=host PROJECT=$* \ V=$(V) out=build/host/$* TEST_BUILD=y EMU_BUILD=y $(TEST_FLAG) \ CROSS_COMPILE= build/host/$*/$*.exe $(run-test-targets): run-%: host-% $(call quiet,host_test,TEST ) .PHONY: hosttests runtests hosttests: $(host-test-targets) runtests: $(run-test-targets) # Automatically enumerate all suites. cts_excludes := common cts_suites := $(filter-out $(cts_excludes), \ $(shell find cts -maxdepth 1 -mindepth 1 -type d -printf "%f ")) # Add boards below as CTS is expanded. cts_boards := stm32l476g-eval nucleo-f072rb .PHONY: build_cts # Create CTS rule automatically for given suite and board # $1: suite name # $2: board name define make-cts = build_cts: cts-$(1)-$(2) cts-$(1)-$(2): $$(MAKE) CTS_MODULE=$(1) BOARD=$(2) # Do not remove this blank line endef # Create rules for all cts-suite-board combinations. Additionally, we serialize # targets per board: cts-x-board -> cts-y-board -> ... # If we don't serialize targets, parallel make fails because all suites # try to produce ec.bin in the same directory (e.g. build/stm32l476g-eval). $(foreach b, $(cts_boards), \ $(foreach s, $(cts_suites), \ $(eval $(call make-cts,$(s),$(b))) \ ) \ ) cov-test-targets=$(foreach t,$(test-list-host),build/host/$(t).info) bldversion=$(shell (./util/getversion.sh ; echo VERSION) | $(CPP) -P) # lcov fails when multiple instances run at the same time. # We need to run them sequentially by using flock cmd_lcov=flock /tmp/ec-lcov-lock -c "lcov -q -o $@ -c -d build/host/$*" cmd_report_cov=genhtml -q -o build/host/coverage_rpt -t \ "EC Unittest "$(bldversion) $^ build/host/%.info: run-% $(call quiet,lcov,COV ) .PHONY: coverage coverage: TEST_FLAG=TEST_COVERAGE=y coverage: $(cov-test-targets) $(call quiet,report_cov,REPORT ) $(out)/firmware_image.lds: common/firmware_image.lds.S $(call quiet,lds,LDS ) $(out)/%.lds: core/$(CORE)/ec.lds.S $(call quiet,lds,LDS ) $(out)/%_B.lds: core/$(CORE)/ec.lds.S $(call quiet,lds_b,LDS_B ) $(out)/%.bin: $(out)/%.obj $(call quiet,obj_to_bin,OBJCOPY) $(if $(wildcard $(PEM)),$(call quiet,rsasign,SIGN ),) $(if $(wildcard $(PEM)),$(call quiet,extractrw-y,EXTR_RW), \ $(call quiet,copyrw-y,COPY_RW)) $(call quiet,mv_from_tmp,MV ) flat-y := $(out)/RW/$(PROJECT).RW.flat flat-$(CONFIG_FW_INCLUDE_RO) += $(out)/RO/$(PROJECT).RO.flat deps += $(out)/firmware_image.lds.d $(flat-y:%.flat=%.lds.d) flat-$(CONFIG_SHAREDLIB) += $(libsharedobjs-y) $(out)/$(PROJECT).obj: common/firmware_image.S $(out)/firmware_image.lds \ $(flat-y) $(call quiet,flat_to_obj,CAT ) $(out)/%.dis: $(out)/%.elf $(call quiet,elf_to_dis,OBJDUMP) $(out)/RW/%.hex: $(out)/RW/%.elf $(out)/RW/%.smap $(call quiet,elf_to_hex,OBJCOPY) ifeq ($(SIGNED_IMAGES),) $(out)/%.flat: $(out)/%.elf $(out)/%.smap utils-build $(call quiet,ec_elf_to_flat,OBJCOPY) $(out)/RO/%.hex: $(out)/RO/%.elf $(out)/RO/%.smap $(call quiet,elf_to_hex,OBJCOPY) else $(out)/RO/%.flat: $(out)/RO/%.elf $(out)/RO/%.smap $(call quiet,elf_to_signed,RO_SIGN,$(CR50_RO_KEY)) $(out)/RW/%.flat: $(out)/RW/%.elf $(out)/RW/%.smap $(call quiet,elf_to_signed,RW_SIGN,$(CR50_RW_KEY)) $(out)/RO/%.hex: $(out)/RO/%.flat $(call quiet,bin_to_hex,OBJCOPY) endif $(out)/$(PROJECT).hex: $(out)/$(PROJECT).bin $(call quiet,bin_to_hex,OBJCOPY) $(out)/RW/%.elf: override BLD:=RW $(out)/RW/%.elf: private objs := $(rw-objs) $(out)/RW/%.elf: $(out)/RW/%.lds $(rw-objs) $(libsharedobjs_elf-y) $(call quiet,elf,LD ) $(out)/RO/%.elf: override BLD:=RO $(out)/RO/%.elf: private objs := $(ro-objs) $(out)/RO/%.elf: $(out)/RO/%.lds $(ro-objs) $(libsharedobjs_elf-y) $(call quiet,elf,LD ) $(out)/%.elf: $(out)/%.lds $(objs) $(call quiet,elf,LD ) $(out)/$(SHOBJLIB)/$(SHOBJLIB).elf: $(sharedlib-objs) @mkdir -p $(out)/$(SHOBJLIB) $(call quiet,sharedlib_elf,LD ) $(out)/%.smap: $(out)/%.elf $(call quiet,smap,NM ) $(out)/$(PROJECT).exe: $(ro-objs) $(call quiet,exe,EXE ) $(out)/RO/%.o:%.c $(call quiet,c_to_o,CC ) $(out)/RW/%.o:%.c $(call quiet,c_to_o,CC ) $(out)/$(SHOBJLIB)/%.o: override LATE_CFLAGS_DEFINE:=-DSHAREDLIB_IMAGE $(out)/$(SHOBJLIB)/%.o:%.c $(call quiet,c_to_o,CC ) $(out)/RO/%.o:%.S $(call quiet,c_to_o,AS ) $(out)/RW/%.o:%.S $(call quiet,c_to_o,AS ) # Conditionally force the rebuilding of ec_version.h only if it would be # changed. old_version_hash := $(shell cat $(out)/ec_version.h 2> /dev/null | md5sum -) new_version_hash := $(shell BOARD=$(BOARD) ./util/getversion.sh | md5sum -) ifneq ($(old_version_hash),$(new_version_hash)) .PHONY: $(out)/ec_version.h endif # All of the objects have an order only dependency on the ec_version header. # This ensures that if ec_version.h needs to be built (because it was marked # PHONY above) then it will be rebuilt before any objects. This is important # because some source files will include ec_version.h and fail to compile if # it doesn't already exist. This dependency shouldn't be a normal dependency # because that would cause every object to be rebuilt when ec_version.h # changes, instead of just the ones that actually depend on it. The objects # that truly depend on ec_version.h will have that information encoded in their # .d file. $(ro-objs): | $(out)/ec_version.h $(rw-objs): | $(out)/ec_version.h $(sharedlib-objs): | $(out)/ec_version.h $(out)/ec_version.h: $(call quiet,version,VERSION) $(build-utils): $(out)/%:$(build-srcs) $(call quiet,c_to_build,BUILDCC) $(host-utils): $(out)/%:$(host-srcs) $(call quiet,c_to_host,HOSTCC ) $(out)/cscope.files: $(out)/$(PROJECT).bin $(call quiet,deps_to_list,SH ) $(out)/TAGS: $(out)/cscope.files $(call quiet,etags,ETAGS ) $(out)/tags: $(out)/cscope.files $(call quiet,ctags,CTAGS ) # TODO: optional make rules for PROJECT_EXTRA $(npcx-flash-fw-bin): $(if $(V),,@echo ' EXTBIN ' $(subst $(out)/,,$@) ; ) -@ mkdir -p $(@D) -@ $(CC) $(CFLAGS) -MMD -MF $(out)/$(npcx-lfw).d -c $(npcx-flash-fw).c \ -o $(out)/$(npcx-flash-fw).o -@ $(CC) $(out)/$(npcx-flash-fw).o $(LDFLAGS) \ -o $(out)/$(npcx-flash-fw).elf -Wl,-T,$(npcx-flash-fw).ld \ -Wl,-Map,$(out)/$(npcx-flash-fw).map -@ $(OBJCOPY) -O binary $(out)/$(npcx-flash-fw).elf $@ .PHONY: xrefs xrefs: $(call targ_if_prog,etags,$(out)/TAGS) \ $(call targ_if_prog,ctags,$(out)/tags) .PHONY: flash flash: $(out)/ec.bin openocd -c "set BOARD $(BOARD)"\ -c "set BUILD_DIR $(out)"\ -f $(BDIR)/openocd-flash.cfg .PHONY: flash_ec flash_ec: $(out)/ec.bin ./util/flash_ec --board $(BOARD) --image $(out)/ec.bin .PHONY: flash_dfu flash_dfu: $(out)/ec.bin sudo ./$(BDIR)/dfu $(out)/ec.bin .PHONY: clean clean: -rm -rf $(out) .PHONY: clobber clobber: -rm -rf build TAGS cscope.files cscope.out .PHONY: help help: @echo "Google Chromium EC build" @echo "Common Targets:" @echo " all [BOARD=] - Build a single board (Default target)" @echo " clean [BOARD=] - Clean a single board" @echo " buildall - Build and test all boards" @echo " clobber - Clean all boards" @echo " proj- - Build a single board (similar to 'all BOARD=boardname')" @echo " savesizes - Save the filesizes of currently built boards for comparison" @echo " newsizes - Compare previously saved filesizes against new sizes" @echo "Common Variables:" @echo " BOARD= - Set the board name to build (Default is $(BOARD))" @echo " CROSS_COMPILE= - Set the compiler for the board" @echo " V=1 - Show make output" @echo "Example:" @echo " make BOARD=reef CROSS_COMPILE='arm-eabi-'" .PHONY: savesizes savesizes: @find $(BUILD_DIR) -name '*.flat' -printf "%s %p\n" | sort --key 2 > \ $(FLATSIZE_FILE) @if [ -s $(FLATSIZE_FILE) ]; then \ echo "Saved sizes for $$(cat $(FLATSIZE_FILE) | wc -l) files"; \ else \ echo "Error: No file sizes saved. Are they built?"; \ fi .PHONY: newsizes newsizes: @if [ ! -s "$(FLATSIZE_FILE)" ]; then \ echo "Error: no saved size file ($(FLATSIZE_FILE))."; \ echo " Run 'make savesizes' first"; \ exit 1; \ fi @FILES_CHANGED=0; \ FILES_IN_LIST=0; \ FILES_COMPARED=0; \ FILE_SIZE_CHANGE=0; \ NEW_SIZES=$$(find $(BUILD_DIR) -name '*.flat' -printf "%s %p\n"); \ while read -r -u 10 line; do \ FILES_IN_LIST=$$((FILES_IN_LIST+1)); \ FLATFILE=$$(echo "$$line" | cut -f2 -d ' '); \ FLATSIZE_ORG=$$(echo "$$line" | cut -f1 -d ' '); \ FLATSIZE_NEW="$$(grep "$$FLATFILE" <<< "$$NEW_SIZES" | \ sed 's/ .*$$//')"; \ if [ -n "$$FLATSIZE_NEW" ]; then \ FILES_COMPARED=$$((FILES_COMPARED+1)); \ if [ "$$FLATSIZE_NEW" -gt "$$FLATSIZE_ORG" ]; then \ FILES_CHANGED=$$((FILES_CHANGED+1)); \ FILE_SIZE_CHANGE=$$((FILE_SIZE_CHANGE+ \ FLATSIZE_NEW-FLATSIZE_ORG)); \ printf "%s grew by %s bytes: (%d to %d)\n" \ "$$FLATFILE" \ "$$((FLATSIZE_NEW-FLATSIZE_ORG))" \ "$$FLATSIZE_ORG" "$$FLATSIZE_NEW"; \ elif [ "$$FLATSIZE_NEW" -lt "$$FLATSIZE_ORG" ]; then \ FILES_CHANGED=$$((FILES_CHANGED+1)); \ FILE_SIZE_CHANGE=$$((FILE_SIZE_CHANGE+ \ FLATSIZE_NEW-FLATSIZE_ORG)); \ printf "%s shrank by %s bytes: (%d to %d)\n" \ "$$FLATFILE" \ "$$((FLATSIZE_ORG-FLATSIZE_NEW))" \ "$$FLATSIZE_ORG" "$$FLATSIZE_NEW"; \ fi; \ fi; \ done 10< "$(FLATSIZE_FILE)"; \ echo "Compared $$FILES_COMPARED of $$FILES_IN_LIST files."; \ if [ $$FILES_COMPARED -ne 0 ] && [ $$FILES_CHANGED -eq 0 ]; then \ echo "File sizes are unchanged."; \ else \ printf "%d files changed.\n" "$$FILES_CHANGED"; \ printf "Total size change: %s bytes.\n" "$$FILE_SIZE_CHANGE"; \ printf "Average size change: %d bytes.\n" \ "$$((FILE_SIZE_CHANGE / FILES_CHANGED))"; \ fi .SECONDARY: -include $(deps)