chromium/native_client_sdk/src/tools/common.mk

# Copyright 2012 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

#
# GNU Make based build file.  For details on GNU Make see:
#   http://www.gnu.org/software/make/manual/make.html
#

#
# Toolchain
#
# By default the VALID_TOOLCHAINS list contains pnacl, clang-newlib and glibc.
# If your project only builds in one or the other then this should be overridden
# accordingly.
#
ALL_TOOLCHAINS ?= pnacl glibc clang-newlib

VALID_TOOLCHAINS ?= $(ALL_TOOLCHAINS)
TOOLCHAIN ?= $(word 1,$(VALID_TOOLCHAINS))

#
# Top Make file, which we want to trigger a rebuild on if it changes
#
TOP_MAKE := $(word 1,$(MAKEFILE_LIST))


#
# Figure out which OS we are running on.
#
GETOS := python $(NACL_SDK_ROOT)/tools/getos.py
NACL_CONFIG := python $(NACL_SDK_ROOT)/tools/nacl_config.py
FIXDEPS := python $(NACL_SDK_ROOT)/tools/fix_deps.py -c
OSNAME := $(shell $(GETOS))
SYSARCH := $(shell $(GETOS) --nacl-arch)


#
# TOOLCHAIN=all recursively calls this Makefile for all VALID_TOOLCHAINS.
#
ifeq ($(TOOLCHAIN),all)

# Define the default target
all:

#
# Generate a new MAKE command for each TOOLCHAIN.
#
# Note: We use targets for each toolchain (instead of an explicit recipe) so
# each toolchain can be built in parallel.
#
# $1 = Toolchain Name
#
define TOOLCHAIN_RULE
TOOLCHAIN_TARGETS += $(1)_TARGET
.PHONY: $(1)_TARGET
$(1)_TARGET:
	+$(MAKE) TOOLCHAIN=$(1) $(MAKECMDGOALS)
endef

#
# The target for all versions
#
USABLE_TOOLCHAINS=$(filter $(OSNAME) $(ALL_TOOLCHAINS),$(VALID_TOOLCHAINS))

ifeq ($(NO_HOST_BUILDS),1)
USABLE_TOOLCHAINS:=$(filter-out $(OSNAME),$(USABLE_TOOLCHAINS))
endif

# Define the toolchain targets for all usable toolchains via the macro.
$(foreach tool,$(USABLE_TOOLCHAINS),$(eval $(call TOOLCHAIN_RULE,$(tool))))

.PHONY: all clean install
all: $(TOOLCHAIN_TARGETS)
clean: $(TOOLCHAIN_TARGETS)
install: $(TOOLCHAIN_TARGETS)

else  # TOOLCHAIN=all

#
# Verify we selected a valid toolchain for this example
#
ifeq (,$(findstring $(TOOLCHAIN),$(VALID_TOOLCHAINS)))

# Only fail to build if this is a top-level make. When building recursively, we
# don't care if an example can't build with this toolchain.
ifeq ($(MAKELEVEL),0)
  $(warning Availbile choices are: $(VALID_TOOLCHAINS))
  $(error Can not use TOOLCHAIN=$(TOOLCHAIN) on this example.)
else

# Dummy targets for recursive make with unsupported toolchain...
.PHONY: all clean install
all:
clean:
install:

endif

else  # TOOLCHAIN is valid...

#
# Build Configuration
#
# The SDK provides two sets of libraries, Debug and Release.  Debug libraries
# are compiled without optimizations to make debugging easier.  By default
# this will build a Release configuration. When debugging via "make debug",
# build the debug configuration by default instead.
#
ifneq (,$(findstring debug,$(MAKECMDGOALS)))
CONFIG ?= Debug
else
CONFIG ?= Release
endif


#
# Verify we selected a valid configuration for this example.
#
VALID_CONFIGS ?= Debug Release
ifeq (,$(findstring $(CONFIG),$(VALID_CONFIGS)))
  $(warning Availbile choices are: $(VALID_CONFIGS))
  $(error Can not use CONFIG=$(CONFIG) on this example.)
endif


#
# Note for Windows:
#   The GCC and LLVM toolchains (include the version of Make.exe that comes
# with the SDK) expect and are capable of dealing with the '/' seperator.
# For this reason, the tools in the SDK, including Makefiles and build scripts
# have a preference for POSIX style command-line arguments.
#
# Keep in mind however that the shell is responsible for command-line escaping,
# globbing, and variable expansion, so those may change based on which shell
# is used.  For Cygwin shells this can include automatic and incorrect expansion
# of response files (files starting with '@').
#
# Disable DOS PATH warning when using Cygwin based NaCl tools on Windows.
#
ifeq ($(OSNAME),win)
  # Always use cmd.exe as the shell on Windows. Otherwise Make may try to
  # search the path for sh.exe. If it is found in a path with a space, the
  # command will fail.
  SHELL := cmd.exe
  CYGWIN ?= nodosfilewarning
  export CYGWIN
endif


#
# If NACL_SDK_ROOT is not already set, then set it relative to this makefile.
#
THIS_MAKEFILE := $(CURDIR)/$(lastword $(MAKEFILE_LIST))
NACL_SDK_ROOT ?= $(realpath $(dir $(THIS_MAKEFILE))/..)


#
# Check that NACL_SDK_ROOT is set to a valid location.
# We use the existence of tools/oshelpers.py to verify the validity of the SDK
# root.
#
ifeq (,$(wildcard $(NACL_SDK_ROOT)/tools/oshelpers.py))
  $(error NACL_SDK_ROOT is set to an invalid location: $(NACL_SDK_ROOT))
endif


#
# If this makefile is part of a valid nacl SDK, but NACL_SDK_ROOT is set
# to a different location this is almost certainly a local configuration
# error.
#
LOCAL_ROOT := $(realpath $(dir $(THIS_MAKEFILE))/..)
ifneq (,$(wildcard $(LOCAL_ROOT)/tools/oshelpers.py))
  ifneq ($(realpath $(NACL_SDK_ROOT)), $(realpath $(LOCAL_ROOT)))
    $(error common.mk included from an SDK that does not match the current NACL_SDK_ROOT)
  endif
endif


#
# Alias for standard POSIX file system commands
#
OSHELPERS = python $(NACL_SDK_ROOT)/tools/oshelpers.py
WHICH := $(OSHELPERS) which
ifdef V
RM := $(OSHELPERS) rm
CP := $(OSHELPERS) cp
MKDIR := $(OSHELPERS) mkdir
MV := $(OSHELPERS) mv
else
RM := @$(OSHELPERS) rm
CP := @$(OSHELPERS) cp
MKDIR := @$(OSHELPERS) mkdir
MV := @$(OSHELPERS) mv
endif



#
# Compute path to requested NaCl Toolchain
#
TC_PATH := $(abspath $(NACL_SDK_ROOT)/toolchain)


#
# Check for required minimum SDK version.
# A makefile can declare NACL_SDK_VERSION_MIN of the form "<major>.<position>",
# where <major> is the major Chromium version number, and <position> is the
# Chromium Cr-Commit-Position number. eg. "39.295386".
#
ifdef NACL_SDK_VERSION_MIN
  VERSION_CHECK:=$(shell $(GETOS) --check-version=$(NACL_SDK_VERSION_MIN) 2>&1)
  ifneq ($(VERSION_CHECK),)
    $(error $(VERSION_CHECK))
  endif
endif


#
# The default target
#
# If no targets are specified on the command-line, the first target listed in
# the makefile becomes the default target.  By convention this is usually called
# the 'all' target.  Here we leave it blank to be first, but define it later
#
all:
.PHONY: all


#
# The install target is used to install built libraries to their final destination.
# By default this is the NaCl SDK 'lib' folder.
#
install:
.PHONY: install

ifdef SEL_LDR
STANDALONE = 1
endif

OUTBASE ?= .
CONFIG_DIR := $(CONFIG)
ifdef STANDALONE
CONFIG_DIR := standalone_$(CONFIG_DIR)
endif
ifdef MSAN
CONFIG_DIR := msan_$(CONFIG_DIR)
endif
ifdef TSAN
CONFIG_DIR := tsan_$(CONFIG_DIR)
endif
ifdef ASAN
CONFIG_DIR := asan_$(CONFIG_DIR)
endif

OUTDIR := $(OUTBASE)/$(TOOLCHAIN)/$(CONFIG_DIR)
STAMPDIR ?= $(OUTDIR)
LIBDIR ?= $(NACL_SDK_ROOT)/lib


#
# Target to remove temporary files
#
.PHONY: clean
clean:
	$(RM) -f $(TARGET).nmf
	$(RM) -rf $(OUTDIR)
	$(RM) -rf user-data-dir


#
# Rules for output directories.
#
# Output will be places in a directory name based on Toolchain and configuration
# be default this will be "newlib/Debug".  We use a python wrapped MKDIR to
# proivde a cross platform solution. The use of '|' checks for existence instead
# of timestamp, since the directory can update when files change.
#
%dir.stamp :
	$(MKDIR) -p $(dir $@)
	@echo Directory Stamp > $@


#
# Dependency Macro
#
# $1 = Name of stamp
# $2 = Directory for the sub-make
# $3 = Extra Settings
#
define DEPEND_RULE
ifndef IGNORE_DEPS
.PHONY: rebuild_$(1)

rebuild_$(1) :| $(STAMPDIR)/dir.stamp
ifeq (,$(2))
	+$(MAKE) -C $(NACL_SDK_ROOT)/src/$(1) STAMPDIR=$(abspath $(STAMPDIR)) $(abspath $(STAMPDIR)/$(1).stamp) $(3)
else
	+$(MAKE) -C $(2) STAMPDIR=$(abspath $(STAMPDIR)) $(abspath $(STAMPDIR)/$(1).stamp) $(3)
endif

all: rebuild_$(1)
$(STAMPDIR)/$(1).stamp: rebuild_$(1)

else

.PHONY: $(STAMPDIR)/$(1).stamp
$(STAMPDIR)/$(1).stamp:
	@echo Ignore $(1)
endif
endef

ifeq ($(TOOLCHAIN),win)
ifdef STANDALONE
HOST_EXT = .exe
else
HOST_EXT = .dll
endif
else
ifdef STANDALONE
HOST_EXT =
else
HOST_EXT = .so
endif
endif


#
# Common Compile Options
#
# For example, -DNDEBUG is added to release builds by default
# so that calls to assert(3) are not included in the build.
#
ifeq ($(CONFIG),Release)
POSIX_CFLAGS ?= -g -O2 -pthread -MMD -DNDEBUG
NACL_LDFLAGS ?= -O2
PNACL_LDFLAGS ?= -O2
else
POSIX_CFLAGS ?= -g -O0 -pthread -MMD -DNACL_SDK_DEBUG
endif

NACL_CFLAGS ?= -Wno-long-long -Werror
NACL_CXXFLAGS ?= -Wno-long-long -Werror
NACL_LDFLAGS += -Wl,-as-needed -pthread

#
# Default Paths
#
INC_PATHS := $(shell $(NACL_CONFIG) -t $(TOOLCHAIN) --include-dirs) $(EXTRA_INC_PATHS)
LIB_PATHS := $(NACL_SDK_ROOT)/lib $(EXTRA_LIB_PATHS)

#
# Define a LOG macro that allow a command to be run in quiet mode where
# the command echoed is not the same as the actual command executed.
# The primary use case for this is to avoid echoing the full compiler
# and linker command in the default case.  Defining V=1 will restore
# the verbose behavior
#
# $1 = The name of the tool being run
# $2 = The target file being built
# $3 = The full command to run
#
ifdef V
define LOG
$(3)
endef
else
ifeq ($(OSNAME),win)
define LOG
@echo   $(1) $(2) && $(3)
endef
else
define LOG
@echo "  $(1) $(2)" && $(3)
endef
endif
endif


#
# Convert a source path to a object file path.
# If source path is absolute then just use the basename of for the object
# file name (absolute sources paths with the same basename are not allowed).
# For relative paths use the full path to the source in the object file path
# name.
#
# $1 = Source Name
# $2 = Arch suffix
#
define SRC_TO_OBJ
$(if $(filter /%,$(1)), $(OUTDIR)/$(basename $(notdir $(1)))$(2).o, $(OUTDIR)/$(basename $(subst ..,__,$(1)))$(2).o)
endef


#
# Convert a source path to a dependency file path.
# We use the .deps extension for dependencies.  These files are generated by
# fix_deps.py based on the .d files which gcc generates.  We don't reference
# the .d files directly so that we can avoid the the case where the compile
# failed but still generated a .d file (in that case the .d file would not
# be processed by fix_deps.py)
#
# $1 = Source Name
# $2 = Arch suffix
#
define SRC_TO_DEP
$(patsubst %.o,%.deps,$(call SRC_TO_OBJ,$(1),$(2)))
endef

#
# The gcc-generated deps files end in .d
#
define SRC_TO_DEP_PRE_FIXUP
$(patsubst %.o,%.d,$(call SRC_TO_OBJ,$(1),$(2)))
endef


#
# If the requested toolchain is a NaCl or PNaCl toolchain, the use the
# macros and targets defined in nacl.mk, otherwise use the host sepecific
# macros and targets.
#
ifneq (,$(findstring $(TOOLCHAIN),linux mac))
include $(NACL_SDK_ROOT)/tools/host_gcc.mk
endif

ifneq (,$(findstring $(TOOLCHAIN),win))
include $(NACL_SDK_ROOT)/tools/host_vc.mk
endif

ifneq (,$(findstring $(TOOLCHAIN),glibc clang-newlib))
include $(NACL_SDK_ROOT)/tools/nacl_gcc.mk
endif

ifneq (,$(findstring $(TOOLCHAIN),pnacl))
include $(NACL_SDK_ROOT)/tools/nacl_llvm.mk
endif

#
# File to redirect to to in order to hide output.
#
ifeq ($(OSNAME),win)
DEV_NULL = nul
else
DEV_NULL = /dev/null
endif


#
# Variables for running examples with Chrome.
#
RUN_PY := python $(NACL_SDK_ROOT)/tools/run.py
HTTPD_PY := python $(NACL_SDK_ROOT)/tools/httpd.py

# Add this to launch Chrome with additional environment variables defined.
# Each element should be specified as KEY=VALUE, with whitespace separating
# key-value pairs. e.g.
# CHROME_ENV=FOO=1 BAR=2 BAZ=3
CHROME_ENV ?=

# Additional arguments to pass to Chrome.
CHROME_ARGS += --enable-nacl --enable-pnacl --no-first-run
CHROME_ARGS += --user-data-dir=$(CURDIR)/user-data-dir


# Paths to Debug and Release versions of the Host Pepper plugins
PPAPI_DEBUG = $(abspath $(OSNAME)/Debug/$(TARGET)$(HOST_EXT));application/x-ppapi-debug
PPAPI_RELEASE = $(abspath $(OSNAME)/Release/$(TARGET)$(HOST_EXT));application/x-ppapi-release


SEL_LDR_PATH := python $(NACL_SDK_ROOT)/tools/sel_ldr.py

ifndef STANDALONE
#
# Assign a sensible default to CHROME_PATH.
#
CHROME_PATH ?= $(shell $(GETOS) --chrome 2> $(DEV_NULL))

#
# Verify we can find the Chrome executable if we need to launch it.
#

NULL :=
SPACE := $(NULL) # one space after NULL is required
CHROME_PATH_ESCAPE := $(subst $(SPACE),\ ,$(CHROME_PATH))

ifeq ($(OSNAME),win)
  SANDBOX_ARGS := --no-sandbox
endif

GDB_PATH := $(shell $(NACL_CONFIG) -t $(TOOLCHAIN) --tool=gdb)

.PHONY: check_for_chrome
check_for_chrome:
ifeq (,$(wildcard $(CHROME_PATH_ESCAPE)))
	$(warning No valid Chrome found at CHROME_PATH=$(CHROME_PATH))
	$(error Set CHROME_PATH via an environment variable, or command-line.)
else
	$(warning Using chrome at: $(CHROME_PATH))
endif
PAGE ?= index.html
PAGE_TC_CONFIG ?= "$(PAGE)?tc=$(TOOLCHAIN)&config=$(CONFIG)"

.PHONY: run
run: check_for_chrome all $(PAGE)
	$(RUN_PY) -C $(CURDIR) -P $(PAGE_TC_CONFIG) \
	    $(addprefix -E ,$(CHROME_ENV)) -- "$(CHROME_PATH)" \
	    $(CHROME_ARGS) \
	    --allow-command-line-plugins \
	    --register-pepper-plugins="$(PPAPI_DEBUG),$(PPAPI_RELEASE)"

.PHONY: run_package
run_package: check_for_chrome all
	@echo "$(TOOLCHAIN) $(CONFIG)" > $(CURDIR)/run_package_config
	"$(CHROME_PATH)" --load-and-launch-app=$(CURDIR) $(CHROME_ARGS)

GDB_ARGS += -D $(GDB_PATH)
# PNaCl's nexe is acquired with "remote get nexe <path>" instead of the NMF.
ifeq (,$(findstring $(TOOLCHAIN),pnacl))
GDB_ARGS += -D --eval-command="nacl-manifest $(abspath $(OUTDIR))/$(TARGET).nmf"
GDB_ARGS += -D $(GDB_DEBUG_TARGET)
endif

.PHONY: debug
debug: check_for_chrome all $(PAGE)
	$(RUN_PY) $(GDB_ARGS) \
	    -C $(CURDIR) -P $(PAGE_TC_CONFIG) \
	    $(addprefix -E ,$(CHROME_ENV)) -- "$(CHROME_PATH)" \
	    $(CHROME_ARGS) $(SANDBOX_ARGS) --enable-nacl-debug \
	    --allow-command-line-plugins \
	    --register-pepper-plugins="$(PPAPI_DEBUG),$(PPAPI_RELEASE)"

.PHONY: serve
serve: all
	$(HTTPD_PY) -C $(CURDIR)
endif

# uppercase aliases (for backward compatibility)
.PHONY: CHECK_FOR_CHROME DEBUG LAUNCH RUN
CHECK_FOR_CHROME: check_for_chrome
DEBUG: debug
LAUNCH: run
RUN: run

endif  # TOOLCHAIN is valid...

endif  # TOOLCHAIN=all