# Copyright (C) 2007-2014 Apple Inc. All rights reserved.
#
# This document is the property of Apple Inc.
# It is considered confidential and proprietary.
#
# This document may not be reproduced or transmitted in any form,
# in whole or in part, without the express written permission of
# Apple Inc.

###############################################################################
# This is the top-level makefile for everything built out of the iBoot source 
# tree.

# Find out what OS we're building on
export BUILD_OS		:=	$(shell uname -s| tr '[:upper:]' '[:lower:]')
$(info %%% building on OS $(BUILD_OS))

# Find the root of the source tree from the build system's perspective.
# could be $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) with a newer make
# Note that buildit sets SRCROOT to the destination for installsrc, so it can be
# empty which breaks the VALID_APPLICATIONS probe.
ORIGINAL_SRCROOT	:=	$(patsubst %/,%,$(dir $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))))
export SRCROOT		?=	${ORIGINAL_SRCROOT}

# Applications listed in the APPLICATIONS argument are processed one at a 
# time, and the per-application makefile in ./makefiles is invoked.
VALID_APPLICATIONS	:=	$(filter-out .%,$(notdir $(shell find $(ORIGINAL_SRCROOT)/apps -mindepth 1 -maxdepth 1 -type d)))

# Valid applications that should not be built by default
SUPPRESSED_APPLICATIONS	:=

# Options that must not be set if building default or multiple APPLICATIONS
SPECIFIC_OPTIONS	:=	TARGETS CONFIGS

# Default SDK platform
ifeq ($(BUILD_OS),darwin)
export SDK_PLATFORM	?=	iphoneos.internal
 ifeq ($(SDKROOT),)
  SDKROOT_PATH		:=	$(shell xcodebuild -version -sdk $(SDK_PLATFORM) Path)
  export SDKROOT	:=	$(SDKROOT_PATH)
 else
  # SDKROOT may be a canonical name (iphoneos.internal). Normalize into a path.
  SDKROOT_PATH		:=	$(shell xcodebuild -version -sdk $(SDKROOT) Path)
  export SDKROOT	:=	$(SDKROOT_PATH)
  export SDK_PLATFORM	:=	$(shell xcodebuild -version -sdk $(SDKROOT) | head -1 | grep -o '(.*)' | tr -d \(\))
 endif
 
 export SDK_PRODUCT_NAME	:= $(shell xcodebuild -version -sdk $(SDKROOT) ProductName)

 ifeq ($(SDKVERSION),)
  export SDKVERSION	:=	$(shell xcodebuild -version -sdk $(SDK_PLATFORM) SDKVersion | head -1)
 endif
 ifeq ($(PLATFORMROOT),)
  export PLATFORMROOT	:=	$(shell xcodebuild -version -sdk $(SDK_PLATFORM) PlatformPath)
 endif

 export HOST_PLATFORM	?=	macosx
 ifeq ($(HOST_SDKROOT),)
  export HOST_SDKROOT	:=	$(shell xcodebuild -version -sdk $(HOST_PLATFORM) Path)
 endif
endif

export OBJROOT		?=	.
export SYMROOT		?=	.
export INSTALL_DIR	?=	$(DSTROOT)/usr/local/standalone/firmware/

###############################################################################
# No user-serviceable parts below

# Check for spaces in critical paths - we can't handle that
SPACE_CHECK		=	$(filter ALL,$(and $(findstring $(subst ~, ,~),$(1)),$(error $(2))))
SDK_ADVICE		=	A path to a component of the SDK contains one or more spaces; this is not supported.  Please relocate your SDK to a path without spaces
$(call SPACE_CHECK,$(SDKROOT_PATH),$(SDK_ADVICE) (SDKROOT=$(SDKROOT_PATH)))
$(call SPACE_CHECK,$(PLATFORMROOT),$(SDK_ADVICE) (PLATFORMROOT=$(PLATFORMROOT)))
$(call SPACE_CHECK,$(HOST_SDKROOT),$(SDK_ADVICE) (HOST_SDKROOT=$(HOST_SDKROOT)))
DIR_ADVICE		=	A path supplied in a variable contains one or more spaces; this is not supported.
$(call SPACE_CHECK,$(DSTROOT),$(DIR_ADVICE) (DSTROOT=$(DSTROOT)))
$(call SPACE_CHECK,$(OBJROOT),$(DIR_ADVICE) (OBJROOT=$(OBJROOT)))
$(call SPACE_CHECK,$(SYMROOT),$(DIR_ADVICE) (SYMROOT=$(SYMROOT)))
$(call SPACE_CHECK,$(SRCROOT),The iBoot sources are in a folder with one or more spaces in the path; this is not supported (SRCROOT=$(SRCROOT)))

# Check for the existence of required directories
DIRECTORY_CHECK		=	$(filter ALL,$(or $(findstring Directory,$(shell stat -Lf %HT $(1))),$(error $(2))))
DIR_MISSING_ADVICE	=	A path to a component of the SDK specifies a directory that does not exist
ifeq ($(BUILD_OS),darwin)
 $(call DIRECTORY_CHECK,$(SDKROOT_PATH),$(DIR_MISSING_ADVICE) (SDKROOT=$(SDKROOT_PATH)))
 $(call DIRECTORY_CHECK,$(PLATFORMROOT),$(DIR_MISSING_ADVICE) (PLATFORMROOT=$(PLATFORMROOT)))
 $(call DIRECTORY_CHECK,$(HOST_SDKROOT),$(DIR_MISSING_ADVICE) (HOST_SDKROOT=$(HOST_SDKROOT)))
endif

# Force the tools to emit consistent punctuation in diagnostics
export LC_ALL=C

# Work out what we are building
ifneq ($(APPS),)
 # APPS is a shortcut for APPLICATIONS, combine if you're dumb enough to specify both
 override APPLICATIONS	:=	$(strip $(APPLICATIONS) $(APPS))
endif
APPLICATIONS		?=	$(filter-out $(SUPPRESSED_APPLICATIONS), $(VALID_APPLICATIONS))
MAKE_APPLICATIONS	:=	$(filter $(VALID_APPLICATIONS), $(APPLICATIONS))
ERROR_APPLICATIONS	:=	$(filter-out $(VALID_APPLICATIONS), $(APPLICATIONS))

ifneq ($(ERROR_APPLICATIONS),)
$(error Unrecognized application(s) - $(ERROR_APPLICATIONS))
endif

# Protect against application-specific options if we are building multiple
# applications
ifneq ($(strip $(foreach opt,$(SPECIFIC_OPTIONS),$(value $(opt)))),)
ifneq ($(strip $(shell echo $(MAKE_APPLICATIONS) | wc -w)),1)
$(error must not specify multiple (or default) APPLICATIONS if any of [$(strip $(SPECIFIC_OPTIONS))] is set)
endif
endif

# Export the list of standard actions, and define targets for each per application.
# Application Makefiles will in turn generate targets based on STANDARD_ACTIONS
# so that we can tunnel more or less arbitrary targets into main.mk.
export STANDARD_ACTIONS	:=	build install library_list
APPLICATION_TEMPLATE	:=	$(addprefix %-,$(MAKE_APPLICATIONS))
ACTIONS			:=	$(foreach action,$(STANDARD_ACTIONS),$(addprefix $(action)-,$(MAKE_APPLICATIONS)))

# The 'help' target is special, in that applications are responsible for handling
# it directly.
HELP_APPLICATIONS	:=	$(addprefix help-,$(MAKE_APPLICATIONS))
ACTIONS			+=	$(HELP_APPLICATIONS)

# The 'build' targets are special, in that they depend on the non-standard
# 'libraries' target.
BUILD_APPLICATIONS	:=	$(addprefix build-,$(MAKE_APPLICATIONS))

# Library list
export LIBLIST		:=	$(OBJROOT)/build/library_list

# Global header list
export HDRLIST		:=	$(OBJROOT)/build/header_list

# Where generated host tools are to be found
export TOOLS_BIN	:=	$(OBJROOT)/build/tools

# Pick up or generate the XBS tag
ifeq ($(RC_ProjectSourceVersion),)
export XBS_BUILD_TAG	?=	"$(shell echo localbuild...$$(whoami)...$$(git symbolic-ref --short HEAD 2>/dev/null || echo 'detached')_$$(git rev-parse --short HEAD 2>/dev/null)$$([[ $$(expr $$(git status --porcelain 2>/dev/null| egrep -v '^(  |\?\?)' | wc -l)) == '0' ]] || echo '_dirty')...$$(date '+%Y/%m/%d-%H:%M:%S'))"
else
export XBS_BUILD_TAG	:=	"iBoot-$(RC_ProjectSourceVersion)"
endif

# Work out whether we are building under XBS or just buildit
ifneq ($(RC_XBS),)
ifeq ($(RC_BUILDIT),)
$(warning building under XBS)
export BUILDING_UNDER_XBS	:=	true
else
export BUILDING_UNDER_BUILDIT	:=	true
$(warning building under BUILDIT)
endif
endif

# Find our tools, now that we know what we're building and in what environment.
include makefiles/tools.mk

# Verbosity for build rules
VERBOSE			?=	NO
ifeq ($(VERBOSE),YES)
export _v =
else
export _v = @
endif

# Setup for parallel sub-makes based on 2 times number of logical CPUs
ifndef MAKEJOBS
 ifeq ($(BUILD_OS),darwin)
  export MAXJOBS	:=	$(shell echo $$((`/usr/sbin//sysctl -n hw.logicalcpu` * 2)) )
 else
  export MAXJOBS	:=	$(shell echo $$((`nproc` * 2)))
 endif
 ifeq ($(BUILDING_UNDER_XBS),true)
  export MAKEJOBS	:=	--jobs=$(MAXJOBS)
 else
  export MAKEJOBS	:= 	--jobs=$(shell echo $$(( $(MAXJOBS) > 8 ? 8 : $(MAXJOBS))) )
 endif
endif
# Help does not look so good if built parallel...
ifeq ($(MAKECMDGOALS),help)
 export MAKEJOBS = --jobs=1
endif

# debug filename hashes are needed for both library and application build phases
export DEBUG_FILENAME_HASHES	:=	$(shell ./tools/generate_debug_hashes.py --src .)

################################################################################
# Toplevel rules
#
all:		build

ifeq ($(BUILD_OS),darwin)
# By depending on libraries, we get the depended library pass out of the way
# and then application targets can build parallel.
build:		build-headers-and-libraries

# Don't depend on build; the target install knows how to do that.
install:	build-headers-and-libraries
else
# Linux can only build tests for now
build:		tests
endif

# Global headers must be built before the libraries but after we've generated
# the libary list so order them properly with this rule.
build-headers-and-libraries:	$(LIBLIST) build-headers build-libraries

build-libraries:	$(LIBLIST)
	@echo "%%%% building libraries"
	@$(MAKE) $(MAKEJOBS) -f makefiles/libraries.mk build MAKEPHASE=libraries SDKROOT=$(SDKROOT_PATH)

$(LIBLIST):	library_list
	@echo "%%%% making $(LIBLIST)"
	@cat /dev/null `find $(OBJROOT)/build -name *.library_list` | sort | uniq > $(LIBLIST)
	@$(CHECK_LIBLIST) $(LIBLIST)

build-headers:
	@echo "%%%% building global headers"
	@cat /dev/null `find $(OBJROOT)/build -name *.header_list` | sort | uniq > $(HDRLIST)
	@$(MAKE) $(MAKEJOBS) -f makefiles/headers.mk build MAKEPHASE=headers SDKROOT=$(SDKROOT_PATH)

clean:
	$(_v)rm -rf $(OBJROOT)/build 
	$(_v)rm -rf $(SYMROOT)/build

pre-help:
	@echo "%%% printing help"
	@echo "Valid APPLICATIONS"
	@echo "    ${VALID_APPLICATIONS}"
	@echo "Default APPLICATIONS"
	@echo "    ${MAKE_APPLICATIONS}"
	@echo ""

help:	pre-help $(HELP_APPLICATIONS)

build-tools:
	@echo "%%%% generating build tools"
	@$(MAKE) -f tools/Makefile build-tools

install-tools:
	@echo Generating install tools
	@$(MAKE) -f tools/Makefile install-tools

installsrc:
	ditto . $(SRCROOT)

installhdrs:
	@echo no headers installed

build-tests:
	@$(MAKE) $(MAKEJOBS) -f makefiles/tests.mk SDKROOT=$(SDKROOT_PATH) build

.PHONY: tests
tests:
	@$(MAKE) $(MAKEJOBS) -f makefiles/tests.mk SDKROOT=$(SDKROOT_PATH) run

cscope.files:
	@echo "Building file list for cscope and tags"
	@find . -name '*.h' -type f | grep -v ^..build > _cscope.files 2> /dev/null
	@find . -name '*.c' -type f | grep -v ^..build >> _cscope.files 2> /dev/null
	@find . -name '*.cpp' -type f | grep -v ^..build >> _cscope.files 2> /dev/null
	@find . -name '*.S' -type f | grep -v ^..build >> _cscope.files 2> /dev/null
	@echo > cscope.files 2> /dev/null
	@sort -u < _cscope.files >> cscope.files 2> /dev/null
	@rm -f _cscope.files _cscope.files2 2> /dev/null

cscope: cscope.files
	@echo "Building cscope database"
	@cscope -bvU 2> /dev/null

spotless realclean:
	rm -rf build cscope.*

# Linux only knows how to build tests for now, so elide this on Linux
ifeq ($(BUILD_OS),darwin)
# Define this late, so that manual dependencies stated above for
# the standard actions are satisfied before the automatic depdencies.
$(STANDARD_ACTIONS)	: % :	$(APPLICATION_TEMPLATE)

# Build actions require the build-libraries target, which is otherwise only seen as
# a co-dependency of the build target
$(BUILD_APPLICATIONS):	build-libraries

$(ACTIONS):	action		=	$(word 1, $(subst -, ,$@))
$(ACTIONS):	application	=	$(word 2, $(subst -, ,$@))
$(ACTIONS):
	@echo "%%%%%% $(action) $(application)"
	@$(MAKE) $(MAKEJOBS) -f apps/$(application)/$(application).mk $(action) SDKROOT=$(SDKROOT_PATH) APPLICATION=$(application) MAKEPHASE=$(action)
endif
