blob: db47e3c29427f81c65dce4bfeb222dd330750bcf [file] [log] [blame]
# **********************************************************
# Copyright (c) 2010-2014 Google, Inc. All rights reserved.
# Copyright (c) 2009-2010 VMware, Inc. All rights reserved.
# **********************************************************
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of VMware, Inc. nor the names of its contributors may be
# used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
###########################################################################
# Enhanced assembly support for CMake projects
#
# Features:
# + Cmake support for asm with Makefiles and Visual Studio.
# + Cross-platform asm using Intel syntax and C pre-processor
# to feed to assembler.
# + Asm code in C/C++ files (separated via #ifdef ASM_CODE_ONLY) to avoid
# needing standalone assembly files with 64-bit cl.
#
###################################################
# Usage:
#
# Setup:
# + Place both this file and cpp2asm_add_newlines.cmake in the same
# directory (or, point at cpp2asm_add_newlines.cmake with the
# variable ${cpp2asm_newline_script_path}).
# + Include this file in your CMakeLists.txt file.
# + For UNIX, the available assembler may not support Intel syntex.
# In that case, CMAKE_ASM_SUPPORTS_INTEL_SYNTAX will be set to false.
# The includer of this file can decide whether that's a fatal error.
#
# Example setup:
# include(cpp2asm_support.cmake)
# if (UNIX)
# if (NOT CMAKE_ASM_SUPPORTS_INTEL_SYNTAX)
# <either FATAL_ERROR or point CMAKE_ASM_COMPILER at a gas binary to use>
# endif ()
# endif (UNIX)
#
# Assembly file:
# + Write assembly using Intel syntax and the macros in the
# cpp2asm_defines.h file included in this directory.
# + Before #include of cpp2asm_defines.h in your asm file, define
# one of ASSEMBLE_WITH_{GAS,MASM,NASM}
# + cpp2asm_defines.h is not automatically added as a dependence.
# If you are using it, pass in its path as part of the extra_deps option.
# + cpp2asm_defines.h's dir is not automatically added as an include dir.
# If you are using it, pass in "-I;<path>" as part of the extra_defs option.
# + The global preprocessor defines are used for Makefile generators.
# For Visual Studio generators, pass in the defines needed (as a list)
# as part of the extra_defs option. These extra_defs are NOT honored
# for Makefile generators and should be added by the caller to the
# target or global flags.
# + Have assembly files #include a configure.h or other header to obtain
# their defines (this file does not support passing them in via cmdline
# to the preprocessor or the assembler).
# + For pure assembly files, use add_asm_target(); for split C/C++ + asm files,
# separate the two pieces via #ifdef ASM_CODE_ONLY and use
# add_split_asm_target().
# + For VS generators, add the returned target as a dependence of the final
# executable/library target to avoid races when building.
#
# Example:
#
# set(asm_deps "/path/to/cpp2asm_defines.h" "/path/to/configure.h")
# set(asm_defs -I "/path/to/cpp2asm_defines.h" -I "/path/to/configure.h")
# add_asm_target(foo.asm foo_asm_src foo_asm_tgt "" "${asm_defs}" ${asm_deps})
# add_library(bar bar.c ${foo_asm_src})
# if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
# add_dependencies(bar ${foo_asm_tgt})
# endif ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
#
###########################################################################
if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio 10")
# For i#801 workaround
cmake_minimum_required(VERSION 2.8.8)
else ()
if (APPLE)
# We want ASM NASM support
cmake_minimum_required(VERSION 2.8.3)
else (APPLE)
# Require 2.6.4 to avoid cmake bug #8639, unless this var is
# properly set (which it is for DR b/c it has a workaround):
if (NOT "${CMAKE_ASM_SOURCE_FILE_EXTENSIONS}" MATCHES "asm")
cmake_minimum_required(VERSION 2.6.4)
endif ()
endif (APPLE)
endif ()
##################################################
# Helper files
if (NOT cpp2asm_newline_script_path)
# By default we expect cpp2asm_add_newlines.cmake in same dir as this file.
get_filename_component(my_dir "${CMAKE_CURRENT_LIST_FILE}" PATH)
set(cpp2asm_newline_script_path "${my_dir}/cpp2asm_add_newlines.cmake")
endif ()
if (NOT EXISTS "${cpp2asm_newline_script_path}")
message(FATAL_ERROR "Cannot find \"${cpp2asm_newline_script_path}\". "
"Please set the cpp2asm_newline_script_path variable.")
endif ()
##################################################
# Preprocessor location and flags
if (UNIX)
# "gcc -E" on a non-.c-extension file gives message:
# "linker input file unused because linking not done"
# and doesn't produce any output, so we must use cpp for our .asm files.
# we assume it's in the same dir.
get_filename_component(compiler_path ${CMAKE_C_COMPILER} PATH)
find_program(CMAKE_CPP cpp HINTS "${compiler_path}" DOC "path to C preprocessor")
if (NOT CMAKE_CPP)
message(FATAL_ERROR "cpp is required to build")
endif (NOT CMAKE_CPP)
mark_as_advanced(CMAKE_CPP)
set(CPP_KEEP_COMMENTS -C)
set(CPP_NO_LINENUM -P)
set(CPP_KEEP_WHITESPACE -traditional-cpp)
set(CMAKE_CPP_FLAGS "")
else (UNIX)
set(CMAKE_CPP ${CMAKE_C_COMPILER})
set(CPP_KEEP_COMMENTS /C)
set(CPP_NO_LINENUM /EP)
set(CPP_KEEP_WHITESPACE "")
set(CMAKE_CPP_FLAGS "/nologo")
endif (UNIX)
##################################################
# Assembler location and flags
if (NOT "${CMAKE_GENERATOR}" MATCHES "Visual Studio")
if (UNIX)
# i#646: cmake 2.8.5 requires ASM-ATT to find as: but we don't want to change
# all our vars to have -ATT suffix so we set the search list instead.
# This must be done prior to enable_language(ASM).
set(CMAKE_ASM_COMPILER_INIT gas as)
endif (UNIX)
# CMake does not support assembly with VS generators
# (http://public.kitware.com/Bug/view.php?id=11536)
# so we have to add our own custom commands and targets
if (APPLE)
# NASM support was added in 2.8.3. It clears ASM_DIALECT for us.
enable_language(ASM_NASM)
else (APPLE)
enable_language(ASM)
endif (APPLE)
endif ()
if (APPLE)
# XXX: we may be able to avoid some of this given CMake 2.8.3's NASM support.
find_program(NASM nasm DOC "path to nasm assembler")
if (NOT NASM)
message(FATAL_ERROR "nasm assembler not found: required to build")
endif (NOT NASM)
message(STATUS "Found nasm: ${NASM}")
execute_process(COMMAND ${NASM} -hf OUTPUT_VARIABLE nasm_result ERROR_QUIET)
if (X64)
# x64 support added between 0.98.40 and 2.10.07
if (NOT nasm_result MATCHES macho64)
message(FATAL_ERROR "nasm is too old: no 64-bit support")
endif ()
set(ASM_FLAGS "${ASM_FLAGS} -fmacho64")
else (X64)
if (nasm_result MATCHES macho32)
set(ASM_FLAGS "${ASM_FLAGS} -fmacho32")
else ()
set(ASM_FLAGS "${ASM_FLAGS} -fmacho")
endif ()
endif (X64)
if (DEBUG)
set(ASM_FLAGS "${ASM_FLAGS} -g")
endif (DEBUG)
elseif (UNIX)
if (X86)
set(ASM_FLAGS "-mmnemonic=intel -msyntax=intel -mnaked-reg")
if (X64)
set(ASM_FLAGS "${ASM_FLAGS} --64")
else (X64)
# putting --32 last so we fail on -mmnemonic=intel on older as, not --32
set(ASM_FLAGS "${ASM_FLAGS} --32")
endif (X64)
elseif (ARM)
# No 64-bit support yet so currently no flags.
endif ()
set(ASM_FLAGS "${ASM_FLAGS} --noexecstack")
if (DEBUG)
set(ASM_FLAGS "${ASM_FLAGS} -g")
endif (DEBUG)
else ()
if (X64)
find_program(CMAKE_ASM_COMPILER ml64.exe HINTS "${cl_path}" DOC "path to assembler")
else (X64)
find_program(CMAKE_ASM_COMPILER ml.exe HINTS "${cl_path}" DOC "path to assembler")
endif (X64)
if (NOT CMAKE_ASM_COMPILER)
message(FATAL_ERROR "assembler not found: required to build")
endif (NOT CMAKE_ASM_COMPILER)
message(STATUS "Found assembler: ${CMAKE_ASM_COMPILER}")
if (NOT DEFINED GENERATE_PDBS OR GENERATE_PDBS)
set(ASM_DBG "/Zi /Zd")
else ()
set(ASM_DBG "")
endif ()
set(ASM_FLAGS "/nologo ${ASM_DBG}")
endif ()
string(REPLACE " " ";" ASM_FLAGS_LIST "${ASM_FLAGS}")
if (APPLE)
# XXX: xcode assembler uses Intel mnemonics but opposite src,dst order!
# We rely on having nasm installed as a workaround.
set(CMAKE_ASM_SUPPORTS_INTEL_SYNTAX ON)
endif (APPLE)
if (UNIX AND NOT APPLE)
# We require gas >= 2.18.50 for --32, --64, and the new -msyntax=intel, etc.
execute_process(COMMAND
${CMAKE_ASM_COMPILER} --help
RESULT_VARIABLE asm_result
ERROR_VARIABLE asm_error
OUTPUT_VARIABLE asm_out)
if (asm_result OR asm_error)
message(FATAL_ERROR "*** ${CMAKE_ASM_COMPILER} failed: ***\n${asm_error}")
endif (asm_result OR asm_error)
# turn the flags into a vector
string(REGEX REPLACE " " ";" flags_needed "${ASM_FLAGS}")
# we want "-mmnemonic=intel" to match "-mmnemonic=[att|intel]"
string(REGEX REPLACE "=" ".*" flags_needed "${flags_needed}")
set(flag_present 1)
foreach (flag ${flags_needed})
if (flag_present)
string(REGEX MATCH "${flag}" flag_present "${asm_out}")
if (NOT flag_present)
message("${CMAKE_ASM_COMPILER} missing flag \"${flag}\"")
endif (NOT flag_present)
endif (flag_present)
endforeach (flag)
# let caller decide whether a fatal error or not
if (NOT flag_present)
set(CMAKE_ASM_SUPPORTS_INTEL_SYNTAX OFF)
else (NOT flag_present)
set(CMAKE_ASM_SUPPORTS_INTEL_SYNTAX ON)
endif (NOT flag_present)
endif (UNIX AND NOT APPLE)
##################################################
# Assembler build rule for Makefile generators
if (APPLE)
# Despite the docs, -o does not work: cpp prints to stdout.
set(CMAKE_ASM_NASM_COMPILE_OBJECT
"${CMAKE_CPP} ${CMAKE_CPP_FLAGS} <FLAGS> <DEFINES> -E <SOURCE> > <OBJECT>.s"
"<CMAKE_COMMAND> -Dfile=<OBJECT>.s -P \"${cpp2asm_newline_script_path}\""
"<NASM> ${ASM_FLAGS} -o <OBJECT> <OBJECT>.s"
)
elseif (UNIX)
# we used to have ".ifdef FOO" and to not have it turn into ".ifdef 1" we'd say
# "-DFOO=FOO", but we now use exclusively preprocessor defines, which is good
# since our defines are mostly in configure.h where we can't as easily tweak them
# (update: I do have top-level defines gathered up in ${defines}).
# so, we don't bother transforming -DFOO into -DFOO=FOO, nor with setting
# up the --defsym args.
set(CMAKE_ASM_COMPILE_OBJECT
"${CMAKE_CPP} ${CMAKE_CPP_FLAGS} <FLAGS> <DEFINES> -E <SOURCE> -o <OBJECT>.s"
"<CMAKE_COMMAND> -Dfile=<OBJECT>.s -P \"${cpp2asm_newline_script_path}\""
# not using <FLAGS> b/c of cmake bug #8107 where -Ddynamorio_EXPORTS
# is passed in: we don't need the include dirs b/c of the cpp step.
# update: Brad fixed bug #8107: moved -Ddynamorio_EXPORTS from <FLAGS> to <DEFINES>
# in CMake/Source/cmMakefileTargetGenerator.cxx:1.115 (will be in 2.6.4).
#
# we also aren't passing any <DEFINES> since for one thing
# there's no way to transform to --defsym: luckily we don't need them
# since using cpp now (see above).
# FIXME: I tried setting CMAKE_ASM_DEFINE_FLAG to "--defsym " (not clear
# how to get =1 in there :should verify it's needed) but <DEFINES>
# comes up empty for me.
"<CMAKE_ASM_COMPILER> ${ASM_FLAGS} -o <OBJECT> <OBJECT>.s"
)
else ()
# Even if we didn't preprocess we'd need our own rule since cmake doesn't
# support ml.
set(CMAKE_ASM_COMPILE_OBJECT
# There's no way to specify a non-default name with /P: writes to
# cwd/sourcebase.i. Could copy with "cmake -E copy" but no way to
# run get_filename_component on a tag var. So going with
# redirection operator which should work in all supported shells.
#
# ml can't handle line number markers so using ${CPP_NO_LINENUM}.
"<CMAKE_C_COMPILER> ${CMAKE_CPP_FLAGS} <FLAGS> <DEFINES> -E ${CPP_NO_LINENUM} <SOURCE> > <OBJECT>.s"
# cmake does add quotes in custom commands, etc. but not in this rule so we add
# them to handle paths with spaces:
"<CMAKE_COMMAND> -Dfile=<OBJECT>.s -P \"${cpp2asm_newline_script_path}\""
"<CMAKE_ASM_COMPILER> ${ASM_FLAGS} /c /Fo<OBJECT> <OBJECT>.s"
)
endif ()
##################################################
# Assembler build commands for Visual Studio generators and
# for assembly code contained in C source files for all generators
# Adds rules to pre-process and assemble the assembly file in ${source}.
# ${extra_defs} should be a list. It is passed to the pre-processor.
# ${extra_deps} should be a list. It is listed as dependencies of the build rule.
# The name of the generated output will be stored in the output_out variable.
# This will be an .obj file for VS or an .asm file for Makefiles, but in either case
# it should be listed as a source of final target executable/library
# If ${extra_suffix} is non-empty, it will be inserted into the output file
# and target name before the extension.
# For VS generators, the caller should add ${tgt_out} to the final target's
# dependencies.
function (add_asm_target source output_out tgt_out extra_suffix extra_defs extra_deps)
if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
get_filename_component(srcbase "${source}" NAME)
set(s_file "${CMAKE_CURRENT_BINARY_DIR}/${srcbase}${extra_suffix}.s")
set_source_files_properties("${s_file}" PROPERTIES GENERATED true)
set(output "${CMAKE_CURRENT_BINARY_DIR}/${srcbase}${extra_suffix}.obj")
# XXX i#801: a VS2010 bug results in custom commands having all their sources
# rebuilt which results in racy collision for asm deps (for DR tests, tools_asm).
# Workaround is to indirect through a dummy .stamp file to avoid
# chaining sources to custom commands, although this will only
# include the .obj file in the target link with cmake 2.8.8+.
# (Alternative is to not list as a source and add to link flags.)
set(output_stamp "${output}.stamp")
set_property(SOURCE "${output}" PROPERTY GENERATED true)
set(output_tgt generate_${srcbase}${extra_suffix})
add_custom_command(OUTPUT "${output_stamp}"
DEPENDS
"${source}"
"${cpp2asm_newline_script_path}"
${extra_deps}
COMMAND ${CMAKE_COMMAND}
ARGS -E touch "${output_stamp}"
COMMAND "${CMAKE_C_COMPILER}" ARGS ${CMAKE_CPP_FLAGS}
/I "${PROJECT_BINARY_DIR}" ${extra_defs}
-E ${CPP_NO_LINENUM} "${source}"
">" "${s_file}"
COMMAND "${CMAKE_COMMAND}" ARGS
-D file=${s_file}
-P "${cpp2asm_newline_script_path}"
# We assume we don't need ${defines} => list on cmdline b/c assembly
# files include configure.h and cpp is where most defines are wanted
COMMAND "${CMAKE_ASM_COMPILER}" ARGS ${ASM_FLAGS_LIST} /c
/Fo "${output}"
"${s_file}"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
VERBATIM # recommended: p260
)
# for correct parallel builds we need a target to avoid
# duplicate and racy VS per-project rules
add_custom_target(${output_tgt} DEPENDS "${output_stamp}")
set(${output_out} "${output}" PARENT_SCOPE)
set(${tgt_out} ${output_tgt} PARENT_SCOPE)
else ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
if (extra_deps)
# XXX: how get cmake to analyze #includes of my asm files?
set_source_files_properties(${source} PROPERTIES OBJECT_DEPENDS
"${cpp2asm_newline_script_path};${extra_deps}")
endif (extra_deps)
# We can't set the extra_defs as source file property b/c caller might
# use same source for two different targets
set(${output_out} "${source}" PARENT_SCOPE)
set(${tgt_out} "" PARENT_SCOPE)
endif ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
endfunction (add_asm_target)
# Adds rules to pre-process and assemble the assembly portion of the C/C++
# file ${source}, which must be separated via #ifdef ASM_CODE_ONLY.
# ${extra_defs} are passed to the pre-processor.
# ${extra_deps} are listed as dependencies of the build rule.
# The name of the generated output will be stored in the output_out variable.
# This will be an .obj file for VS or an .asm file for Makefiles, but in either case
# it should be listed as a source of final target executable/library
# If ${extra_suffix} is non-empty, it will be inserted into the output file
# and target name before the extension.
# For VS generators, the caller should add ${tgt_out} to the final target's
# dependencies.
function (add_split_asm_target source output_out tgt_out
extra_suffix extra_defs extra_deps)
get_filename_component(srcbase ${source} NAME)
set(asm_file "${CMAKE_CURRENT_BINARY_DIR}/${srcbase}${extra_suffix}.asm")
# Be sure to make extra_defs a list!
set(extra_defs -DASM_CODE_ONLY ${extra_defs})
add_asm_target("${asm_file}" asm_output asm_target ""
"${extra_defs}" "${extra_deps}")
if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
add_custom_command(TARGET ${asm_target} PRE_BUILD
COMMAND ${CMAKE_COMMAND}
ARGS -E copy "${source}" "${asm_file}")
else ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
set_source_files_properties(${asm_file} PROPERTIES
GENERATED ON COMPILE_FLAGS "-DASM_CODE_ONLY")
add_custom_command(
OUTPUT ${asm_file}
DEPENDS ${source}
COMMAND ${CMAKE_COMMAND}
ARGS -E copy "${source}" "${asm_file}"
VERBATIM # recommended: p260
)
endif ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
set(${output_out} "${asm_output}" PARENT_SCOPE)
set(${tgt_out} ${asm_target} PARENT_SCOPE)
endfunction(add_split_asm_target)