pkg/maker/buildcontent.go
/*
* Copyright (c) 2024 Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
package maker
import (
"path"
"path/filepath"
"regexp"
"slices"
"strconv"
"strings"
"github.com/Open-CMSIS-Pack/cbuild2cmake/pkg/utils"
sortedmap "github.com/gobs/sortedmap"
)
type BuildFiles struct {
Interface bool
Include ScopeMap
Source LanguageMap
Custom LanguageMap
Library []string
Object []string
PreIncludeLocal []string
}
type CompilerAbstractions struct {
Debug string
Optimize string
Warnings string
LanguageC string
LanguageCpp string
}
type ScopeMap map[string]map[string][]string
type LanguageMap map[string][]string
var CategoryLanguageMap = map[string]string{
"headerAsm": "ASM",
"headerC": "C",
"headerCpp": "CXX",
"includeAsm": "ASM",
"includeC": "C",
"includeCpp": "CXX",
"sourceAsm": "ASM",
"sourceC": "C",
"sourceCpp": "CXX",
}
var LanguageReMap = map[string]string{
"asm": "ASM",
"c": "C",
"cpp": "CXX",
"c-cpp": "C,CXX",
}
func GetLanguage(file Files) string {
language := CategoryLanguageMap[file.Category]
if len(language) > 0 {
return language
}
language = LanguageReMap[file.Language]
if len(language) > 0 {
return language
}
switch path.Ext(file.File) {
case ".c", ".C":
return "C"
case ".cpp", ".c++", ".C++", ".cxx", ".cc", ".CC":
return "CXX"
case ".asm", ".s", ".S":
return "ASM"
}
return "ALL"
}
func GetScope(file Files) string {
if len(file.Scope) > 0 && (file.Scope == "private" || file.Scope == "hidden") {
return "PRIVATE"
}
return "PUBLIC"
}
func ReplaceDelimiters(identifier string) string {
pattern := regexp.MustCompile(`::|:|&|@>=|@|\.|/|\(|\)| `)
return pattern.ReplaceAllString(identifier, "_")
}
func MergeLanguageCommonIncludes(languages LanguageMap) LanguageMap {
intersection := utils.Intersection(languages["C"], languages["CXX"])
if len(intersection) > 0 {
languages["C,CXX"] = utils.AppendUniquely(languages["C,CXX"], intersection...)
languages["C"] = utils.RemoveIncludes(languages["C"], intersection...)
languages["CXX"] = utils.RemoveIncludes(languages["CXX"], intersection...)
}
intersection = utils.Intersection(languages["ASM"], languages["C,CXX"])
if len(intersection) > 0 {
languages["ALL"] = utils.AppendUniquely(languages["ALL"], intersection...)
languages["ASM"] = utils.RemoveIncludes(languages["ASM"], intersection...)
languages["C,CXX"] = utils.RemoveIncludes(languages["C,CXX"], intersection...)
}
languages["C"] = utils.RemoveIncludes(languages["C"], utils.Intersection(languages["ALL"], languages["C"])...)
languages["CXX"] = utils.RemoveIncludes(languages["CXX"], utils.Intersection(languages["ALL"], languages["CXX"])...)
languages["C,CXX"] = utils.RemoveIncludes(languages["C,CXX"], utils.Intersection(languages["ALL"], languages["C,CXX"])...)
languages["ASM"] = utils.RemoveIncludes(languages["ASM"], utils.Intersection(languages["ALL"], languages["ASM"])...)
return languages
}
func CMakeAddLibrary(name string, buildFiles BuildFiles) string {
content := "\nadd_library(" + name
if buildFiles.Interface {
content += " INTERFACE)"
} else {
content += " OBJECT"
for _, language := range sortedmap.AsSortedMap(buildFiles.Source) {
for _, file := range language.Value {
content += "\n \"" + file + "\""
}
}
content += "\n)"
}
return content
}
func (c *Cbuild) CMakeAddLibraryCustomFile(name string, file Files) string {
return "\nadd_library(" + name + " OBJECT\n \"" + AddRootPrefix(c.ContextRoot, file.File) + "\"\n)"
}
func OutputFiles(outputList []Output) (outputByProducts string, outputFile string, outputType string, customCommands string) {
for _, output := range outputList {
switch output.Type {
case "hex":
outputByProducts += "\nset(HEX_FILE \"" + output.File + "\")"
customCommands += "\n\n# Hex Conversion\n add_custom_command(TARGET ${CONTEXT} POST_BUILD COMMAND ${CMAKE_OBJCOPY} ${ELF2HEX})"
case "bin":
outputByProducts += "\nset(BIN_FILE \"" + output.File + "\")"
customCommands += "\n\n# Bin Conversion\n add_custom_command(TARGET ${CONTEXT} POST_BUILD COMMAND ${CMAKE_OBJCOPY} ${ELF2BIN})"
case "cmse-lib":
outputByProducts += "\nset(CMSE_LIB \"" + output.File + "\")"
case "elf", "lib":
outputFile = output.File
outputType = output.Type
}
}
return outputByProducts, outputFile, outputType, customCommands
}
func (m *Maker) AddStepSuffix(name string) string {
if slices.Contains(m.Contexts, name) {
name += "-build"
}
return name
}
func (m *Maker) CMakeTargetAddDependencies(name string, dependencies []string) string {
var content string
if len(dependencies) > 0 {
content += "\nadd_dependencies(" + m.AddStepSuffix(name)
for _, dependency := range dependencies {
content += "\n " + m.AddStepSuffix(dependency)
}
content += "\n)"
}
return content
}
func (m *Maker) BuildDependencies() string {
var content string
for _, cbuild := range m.CbuildIndex.BuildIdx.Cbuilds {
if m.Options.UseContextSet && !slices.Contains(m.Contexts, cbuild.Project+cbuild.Configuration) {
continue
}
content += m.CMakeTargetAddDependencies(cbuild.Project+cbuild.Configuration, cbuild.DependsOn)
}
for _, item := range m.CbuildIndex.BuildIdx.Executes {
content += m.CMakeTargetAddDependencies(item.Execute, item.DependsOn)
}
if len(content) > 0 {
content = "\n\n# Build dependencies" + content
}
return content
}
func CMakeTargetIncludeDirectories(name string, includes ScopeMap) string {
if len(includes) == 0 {
return ""
}
content := "\ntarget_include_directories(" + name
scopeIndentation := " "
fileIndentation := "\n "
if len(includes) > 1 {
scopeIndentation = "\n "
fileIndentation = "\n "
}
for _, scope := range sortedmap.AsSortedMap(includes) {
content += scopeIndentation + scope.Key
var allLanguagesContent, specificLanguageContent string
for _, language := range sortedmap.AsSortedMap(MergeLanguageCommonIncludes(scope.Value)) {
if language.Key == "ALL" {
for _, file := range language.Value {
if strings.Contains(file, "$<TARGET_PROPERTY:") {
content += fileIndentation + file
} else {
allLanguagesContent += fileIndentation + file
}
}
} else {
if len(language.Value) > 0 {
specificLanguageContent += fileIndentation + "$<$<COMPILE_LANGUAGE:" + language.Key + ">:"
for _, file := range language.Value {
specificLanguageContent += fileIndentation + " " + file
}
specificLanguageContent += fileIndentation + ">"
}
}
}
content += specificLanguageContent + allLanguagesContent
}
content += "\n)"
return content
}
func CMakeTargetCompileDefinitions(name string, parent string, scope string, define []interface{}, undefine []string) string {
content := "\ntarget_compile_definitions(" + name + " " + scope
if len(define) > 0 {
content += "\n $<$<COMPILE_LANGUAGE:C,CXX>:\n "
content += ListCompileDefinitions(define, "\n ")
content += "\n >"
}
if len(parent) > 0 {
if len(undefine) > 0 {
content += "\n $<LIST:FILTER,$<TARGET_PROPERTY:" + parent + ",INTERFACE_COMPILE_DEFINITIONS>,EXCLUDE,^" + strings.Join(undefine, ".*,^") + ".*>"
} else {
content += "\n $<TARGET_PROPERTY:" + parent + ",INTERFACE_COMPILE_DEFINITIONS>"
}
}
content += "\n)"
return content
}
func ListIncludeDirectories(includes []string, delimiter string) string {
return strings.Join(includes, delimiter)
}
func ListCompileDefinitions(defines []interface{}, delimiter string) string {
var definesList []string
for _, define := range defines {
key, value := utils.GetDefine(define)
pair := key
if len(value) > 0 {
pair += "=" + value
}
definesList = append(definesList, pair)
}
return strings.Join(definesList, delimiter)
}
func (c *Cbuild) ListGroupsAndComponents() []string {
// get last child group names
groupsAndComponents := c.BuildGroups
// get component names
for _, component := range c.BuildDescType.Components {
groupsAndComponents = append(groupsAndComponents, ReplaceDelimiters(component.Component))
}
return groupsAndComponents
}
func (c *Cbuild) CMakeTargetCompileOptionsGlobal(name string, scope string) string {
// options from context settings
optionsMap := make(map[string][]string)
for _, language := range c.Languages {
prefix := language
if language == "C" {
prefix = "CC"
}
optionsMap[language] = append(optionsMap[language], "${"+prefix+"_CPU}")
optionsMap[language] = append(optionsMap[language], "${"+prefix+"_FLAGS}")
if len(c.BuildDescType.Processor.Trustzone) > 0 {
optionsMap[language] = append(optionsMap[language], "${"+prefix+"_SECURE}")
}
if len(c.BuildDescType.Processor.BranchProtection) > 0 {
optionsMap[language] = append(optionsMap[language], "${"+prefix+"_BRANCHPROT}")
}
if len(c.BuildDescType.Processor.Endian) > 0 {
optionsMap[language] = append(optionsMap[language], "${"+prefix+"_BYTE_ORDER}")
}
}
// add global misc options
c.GetCompileOptionsLanguageMap(c.BuildDescType.Misc, &optionsMap)
// pre-includes global
for _, preInclude := range c.PreIncludeGlobal {
optionsMap["C,CXX"] = append(optionsMap["C,CXX"], "${_PI}\""+preInclude+"\"")
}
// target compile options
content := "\ntarget_compile_options(" + name + " " + scope
for _, language := range sortedmap.AsSortedMap(optionsMap) {
content += c.LanguageSpecificCompileOptions(language.Key, language.Value...)
}
content += "\n)"
return content
}
func (c *Cbuild) CMakeTargetLinkLibraries(name string, scope string, libraries ...string) string {
content := "\ntarget_link_libraries(" + name + " " + scope
for _, library := range libraries {
content += "\n " + library
}
content += "\n)"
return content
}
func (c *Cbuild) CMakeTargetCompileOptions(name string, scope string, misc Misc, preIncludes []string, parent string) string {
content := "\ntarget_compile_options(" + name + " " + scope
content += "\n $<TARGET_PROPERTY:" + parent + ",INTERFACE_COMPILE_OPTIONS>"
optionsMap := make(map[string][]string)
c.GetCompileOptionsLanguageMap(misc, &optionsMap)
for _, preInclude := range preIncludes {
optionsMap["C,CXX"] = append(optionsMap["C,CXX"], "${_PI}\""+preInclude+"\"")
}
for _, language := range sortedmap.AsSortedMap(optionsMap) {
content += c.LanguageSpecificCompileOptions(language.Key, language.Value...)
}
content += "\n)"
return content
}
func (c *Cbuild) CMakeTargetCompileOptionsAbstractions(name string, abstractions CompilerAbstractions, languages []string) string {
content := "\nadd_library(" + name + "_ABSTRACTIONS INTERFACE)"
var options string
for _, language := range languages {
prefix := language
if language == "C" {
prefix = "CC"
}
if !IsAbstractionEmpty(abstractions, language) {
content += "\ncbuild_set_options_flags(" + prefix
content += c.SetOptionsFlags(abstractions, language)
content += " " + prefix + "_OPTIONS_FLAGS_" + name + ")"
options += c.LanguageSpecificCompileOptions(language, "${"+prefix+"_OPTIONS_FLAGS_"+name+"}")
}
}
if len(content) > 0 {
content += "\ntarget_compile_options(" + name + "_ABSTRACTIONS INTERFACE" + options + "\n)"
}
return content
}
func (c *Cbuild) GetCompileOptionsLanguageMap(misc Misc, optionsMap *map[string][]string) {
for _, language := range c.Languages {
switch language {
case "ASM":
if len(misc.ASM) > 0 {
(*optionsMap)[language] = append((*optionsMap)[language], misc.ASM...)
}
case "C", "CXX":
if language == "C" && len(misc.C) > 0 {
(*optionsMap)[language] = append((*optionsMap)[language], misc.C...)
}
if language == "CXX" && len(misc.CPP) > 0 {
(*optionsMap)[language] = append((*optionsMap)[language], misc.CPP...)
}
if len(misc.CCPP) > 0 {
(*optionsMap)[language] = append((*optionsMap)[language], misc.CCPP...)
}
}
}
}
func IsCompileMiscEmpty(misc Misc) bool {
if len(misc.ASM) > 0 || len(misc.C) > 0 || len(misc.CPP) > 0 || len(misc.CCPP) > 0 {
return false
}
return true
}
func AreAbstractionsEmpty(abstractions CompilerAbstractions, languages []string) bool {
for _, language := range languages {
if !IsAbstractionEmpty(abstractions, language) {
return false
}
}
return true
}
func IsAbstractionEmpty(abstractions CompilerAbstractions, language string) bool {
if len(abstractions.Debug) > 0 || len(abstractions.Optimize) > 0 || len(abstractions.Warnings) > 0 ||
(language == "C" && len(abstractions.LanguageC) > 0) ||
(language == "CXX" && len(abstractions.LanguageCpp) > 0) {
return false
}
return true
}
func GetFileOptions(file Files, hasAbstractions bool, delimiter string) string {
var options []string
language := GetLanguage(file)
prefix := language
switch language {
case "ASM":
options = file.Misc.ASM
case "C":
options = append(file.Misc.C, file.Misc.CCPP...)
prefix = "CC"
case "CXX":
options = append(file.Misc.CPP, file.Misc.CCPP...)
}
if hasAbstractions {
options = append(options, "${"+prefix+"_OPTIONS_FLAGS}")
}
return strings.Join(options, delimiter)
}
func (c *Cbuild) LanguageSpecificCompileOptions(language string, options ...string) string {
content := "\n " + "$<$<COMPILE_LANGUAGE:" + language + ">:"
for _, option := range options {
content += "\n " + AddShellPrefix(c.AdjustRelativePath(option))
}
content += "\n >"
return content
}
func AddShellPrefix(input string) string {
return "\"SHELL:" + strings.ReplaceAll(input, "\"", "\\\"") + "\""
}
func AddRootPrefix(base string, input string) string {
if !strings.HasPrefix(input, "${") && !filepath.IsAbs(input) {
return "${SOLUTION_ROOT}/" + path.Join(base, input)
}
return input
}
func AddRootPrefixes(base string, input []string) []string {
var list []string
for _, element := range input {
list = append(list, AddRootPrefix(base, element))
}
return list
}
func (c *Cbuild) ClassifyFiles(files []Files) BuildFiles {
var buildFiles BuildFiles
buildFiles.Include = make(ScopeMap)
buildFiles.Source = make(LanguageMap)
buildFiles.Custom = make(LanguageMap)
buildFiles.Interface = true
for _, file := range files {
if strings.Contains(file.Category, "source") && file.Attr != "template" && !HasFileCustomOptions(file) {
buildFiles.Interface = false
break
}
}
for _, file := range files {
if file.Attr == "template" {
continue
}
switch file.Category {
case "header", "headerAsm", "headerC", "headerCpp", "include", "includeAsm", "includeC", "includeCpp":
var scope string
if buildFiles.Interface {
scope = "INTERFACE"
} else {
scope = GetScope(file)
}
language := GetLanguage(file)
includePath := path.Clean(file.File)
if strings.Contains(file.Category, "header") {
includePath = path.Dir(includePath)
}
if _, ok := buildFiles.Include[scope]; !ok {
buildFiles.Include[scope] = make(LanguageMap)
}
if file.Attr == "config" {
buildFiles.Include[scope][language] = utils.PrependUniquely(buildFiles.Include[scope][language], AddRootPrefix(c.ContextRoot, includePath))
} else {
buildFiles.Include[scope][language] = utils.AppendUniquely(buildFiles.Include[scope][language], AddRootPrefix(c.ContextRoot, includePath))
}
case "source", "sourceAsm", "sourceC", "sourceCpp":
language := GetLanguage(file)
c.AddContextLanguage(language)
if HasFileCustomOptions(file) {
buildFiles.Custom[language] = append(buildFiles.Custom[language], AddRootPrefix(c.ContextRoot, file.File))
} else {
buildFiles.Source[language] = append(buildFiles.Source[language], AddRootPrefix(c.ContextRoot, file.File))
}
case "library":
buildFiles.Library = append(buildFiles.Library, AddRootPrefix(c.ContextRoot, file.File))
case "object":
buildFiles.Object = append(buildFiles.Object, AddRootPrefix(c.ContextRoot, file.File))
case "preIncludeLocal":
buildFiles.PreIncludeLocal = append(buildFiles.PreIncludeLocal, AddRootPrefix(c.ContextRoot, file.File))
case "preIncludeGlobal":
c.PreIncludeGlobal = append(c.PreIncludeGlobal, AddRootPrefix(c.ContextRoot, file.File))
}
}
return buildFiles
}
func (c *Cbuild) MergeIncludes(includes ScopeMap, scope string, parent string, addPaths []string, addPathsAsm []string, delPaths []string) ScopeMap {
if _, ok := includes[scope]; !ok {
includes[scope] = make(LanguageMap)
}
if len(addPaths) > 0 {
includes[scope]["C,CXX"] = utils.AppendUniquely(AddRootPrefixes(c.ContextRoot, addPaths), includes[scope]["C,CXX"]...)
}
if len(addPathsAsm) > 0 {
includes[scope]["ASM"] = utils.AppendUniquely(AddRootPrefixes(c.ContextRoot, addPathsAsm), includes[scope]["ASM"]...)
}
if len(parent) > 0 {
if len(delPaths) > 0 {
includes[scope]["ALL"] = utils.PrependUniquely(includes[scope]["ALL"], "$<LIST:REMOVE_ITEM,$<TARGET_PROPERTY:"+
parent+",INTERFACE_INCLUDE_DIRECTORIES>,"+ListIncludeDirectories(AddRootPrefixes(c.ContextRoot, delPaths), ",")+">")
} else {
includes[scope]["ALL"] = utils.PrependUniquely(includes[scope]["ALL"], "$<TARGET_PROPERTY:"+parent+",INTERFACE_INCLUDE_DIRECTORIES>")
}
}
return includes
}
func AppendGlobalIncludes(includes LanguageMap, elements ScopeMap) LanguageMap {
for scope, languages := range elements {
if scope != "PRIVATE" {
if includes == nil {
includes = make(LanguageMap)
}
for language, paths := range languages {
includes[language] = utils.AppendUniquely(includes[language], paths...)
}
}
}
return includes
}
func (c *Cbuild) ProcessorOptions() string {
content := "\nset(CPU " + c.BuildDescType.Processor.Core + ")"
var FpuMap = map[string]string{
"dp": "DP_FPU",
"sp": "SP_FPU",
"off": "NO_FPU",
}
fpu := FpuMap[c.BuildDescType.Processor.Fpu]
if len(fpu) > 0 {
content += "\nset(FPU " + fpu + ")"
}
var DspMap = map[string]string{
"on": "DSP",
"off": "NO_DSP",
}
dsp := DspMap[c.BuildDescType.Processor.Dsp]
if len(dsp) > 0 {
content += "\nset(DSP " + dsp + ")"
}
var SecureMap = map[string]string{
"secure": "Secure",
"secure-only": "Secure-only",
"non-secure": "Non-secure",
}
secure := SecureMap[c.BuildDescType.Processor.Trustzone]
if len(secure) > 0 {
content += "\nset(SECURE " + secure + ")"
}
var MveMap = map[string]string{
"fp": "FP_FVE",
"int": "MVE",
"off": "NO_MVE",
}
mve := MveMap[c.BuildDescType.Processor.Mve]
if len(mve) > 0 {
content += "\nset(MVE " + mve + ")"
}
var BranchProtectionMap = map[string]string{
"bti": "BTI",
"bti-signret": "BTI_SIGNRET",
"off": "NO_BRANCHPROT",
}
branchProtection := BranchProtectionMap[c.BuildDescType.Processor.BranchProtection]
if len(branchProtection) > 0 {
content += "\nset(BRANCHPROT " + branchProtection + ")"
}
var EndianMap = map[string]string{
"big": "Big-endian",
"little": "Little-endian",
}
endian := EndianMap[c.BuildDescType.Processor.Endian]
if len(endian) > 0 {
content += "\nset(BYTE_ORDER " + endian + ")"
}
return content
}
func InheritCompilerAbstractions(parent CompilerAbstractions, child CompilerAbstractions) CompilerAbstractions {
if len(child.Debug) == 0 {
child.Debug = parent.Debug
}
if len(child.Optimize) == 0 {
child.Optimize = parent.Optimize
}
if len(child.Warnings) == 0 {
child.Warnings = parent.Warnings
}
if len(child.LanguageC) == 0 {
child.LanguageC = parent.LanguageC
}
if len(child.LanguageCpp) == 0 {
child.LanguageCpp = parent.LanguageCpp
}
return child
}
func (c *Cbuild) SetOptionsFlags(abstractions CompilerAbstractions, language string) string {
languageStandard := map[string]string{
"C": abstractions.LanguageC,
"CXX": abstractions.LanguageCpp,
}
flags := []string{
abstractions.Optimize,
abstractions.Debug,
abstractions.Warnings,
languageStandard[language],
}
var content string
for _, flag := range flags {
content += " \""
if len(flag) > 0 {
content += flag
}
content += "\""
}
return content
}
func HasFileAbstractions(files []Files) bool {
hasFileAbstractions := false
for _, file := range files {
if strings.Contains(file.Category, "source") {
fileAbstractions := CompilerAbstractions{file.Debug, file.Optimize, file.Warnings, file.LanguageC, file.LanguageCpp}
hasFileAbstractions = !IsAbstractionEmpty(fileAbstractions, GetLanguage(file))
if hasFileAbstractions {
break
}
}
}
return hasFileAbstractions
}
func HasFileCustomOptions(file Files) bool {
if len(file.AddPath) > 0 || len(file.AddPathAsm) > 0 || len(file.DelPath) > 0 ||
(GetLanguage(file) != "ASM" && (len(file.Define) > 0 || len(file.Undefine) > 0)) {
return true
}
return false
}
func (c *Cbuild) CompilerAbstractions(abstractions CompilerAbstractions, language string) string {
languageStandard := map[string]string{
"C": abstractions.LanguageC,
"CXX": abstractions.LanguageCpp,
}
flags := []string{
abstractions.Optimize,
abstractions.Debug,
abstractions.Warnings,
languageStandard[language],
}
prefix := language
if language == "C" {
prefix = "CC"
}
content := "\nset(" + prefix + "_OPTIONS_FLAGS)"
content += "\ncbuild_set_options_flags(" + prefix
for _, flag := range flags {
content += " \""
if len(flag) > 0 {
content += flag
}
content += "\""
}
content += " " + prefix + "_OPTIONS_FLAGS)"
content += "\nseparate_arguments(" + prefix + "_OPTIONS_FLAGS)"
return content
}
func (c *Cbuild) CMakeSetFileProperties(file Files, abstractions CompilerAbstractions) string {
var content string
// file build options
language := GetLanguage(file)
hasMisc := !IsCompileMiscEmpty(file.Misc)
// file compiler abstractions
hasAbstractions := !IsAbstractionEmpty(abstractions, language)
if hasAbstractions {
content += c.CompilerAbstractions(abstractions, language)
}
// set file properties
if hasMisc || hasAbstractions {
content += "\nset_source_files_properties(\"" + AddRootPrefix(c.ContextRoot, file.File) +
"\" PROPERTIES\n COMPILE_OPTIONS \"" + GetFileOptions(file, hasAbstractions, ";") + "\"\n)"
}
return content
}
func (c *Cbuild) SetFileAsmDefines(file Files, parentMiscAsm []string) string {
var content string
if len(file.DefineAsm) > 0 {
flags := utils.AppendUniquely(parentMiscAsm, file.Misc.ASM...)
if (c.Toolchain == "AC6" || c.Toolchain == "GCC") && path.Ext(file.File) != ".S" && !strings.Contains(utils.FindLast(flags, "-x"), "assembler-with-cpp") {
syntax := "AS_GNU"
masm := utils.FindLast(flags, "-masm")
if c.Toolchain == "AC6" && (strings.Contains(masm, "armasm") || strings.Contains(masm, "auto")) {
syntax = "AS_ARM"
}
content += "\nset(COMPILE_DEFINITIONS\n " + ListCompileDefinitions(file.DefineAsm, "\n ") + "\n)"
content += "\ncbuild_set_defines(" + syntax + " COMPILE_DEFINITIONS)"
content += "\nset_source_files_properties(\"" + AddRootPrefix(c.ContextRoot, file.File) +
"\" PROPERTIES\n COMPILE_FLAGS \"${COMPILE_DEFINITIONS}\"\n)"
} else {
content += "\nset_source_files_properties(\"" + AddRootPrefix(c.ContextRoot, file.File) +
"\" PROPERTIES\n COMPILE_DEFINITIONS \"" + ListCompileDefinitions(file.DefineAsm, ";") + "\"\n)"
}
}
return content
}
func (c *Cbuild) AddContextLanguage(language string) {
if language == "ALL" {
return
}
for _, stored := range c.Languages {
if stored == language {
return
}
}
c.Languages = append(c.Languages, language)
}
func (c *Cbuild) LinkerOptions() (linkerVars string, linkerOptions string) {
linkerVars += "\nset(LD_SCRIPT \"" + AddRootPrefix(c.ContextRoot, c.BuildDescType.Linker.Script) + "\")"
if len(c.BuildDescType.Linker.Regions) > 0 {
linkerVars += "\nset(LD_REGIONS \"" + AddRootPrefix(c.ContextRoot, c.BuildDescType.Linker.Regions) + "\")"
}
if len(c.BuildDescType.Linker.Define) > 0 {
linkerVars += "\nset(LD_SCRIPT_PP_DEFINES\n "
linkerVars += ListCompileDefinitions(c.BuildDescType.Linker.Define, "\n ")
linkerVars += "\n)"
}
linkerOptions += "\n# Linker options\ntarget_link_options(${CONTEXT} PUBLIC\n " +
AddShellPrefix("${LD_CPU}") + "\n " +
AddShellPrefix("${_LS}\"${LD_SCRIPT_PP}\"")
if len(c.BuildDescType.Processor.Trustzone) > 0 {
linkerOptions += "\n " + AddShellPrefix("${LD_SECURE}")
}
options := c.BuildDescType.Misc.Link
for _, language := range c.Languages {
if language == "C" {
options = append(options, c.BuildDescType.Misc.LinkC...)
}
if language == "CXX" {
options = append(options, c.BuildDescType.Misc.LinkCPP...)
}
}
for _, option := range options {
linkerOptions += "\n " + AddShellPrefix(c.AdjustRelativePath(option))
}
linkerOptions += "\n)"
linkerOptions += "\nset_target_properties(${CONTEXT} PROPERTIES LINK_DEPENDS ${LD_SCRIPT})"
if path.Ext(c.BuildDescType.Linker.Script) == ".src" || len(c.BuildDescType.Linker.Regions) > 0 || len(c.BuildDescType.Linker.Define) > 0 {
linkerScriptPP := strings.TrimSuffix(path.Base(c.BuildDescType.Linker.Script), ".src")
linkerVars += "\nset(LD_SCRIPT_PP \"${CMAKE_CURRENT_BINARY_DIR}/" + linkerScriptPP + "\")"
linkerOptions += "\n\n# Linker script pre-processing\nadd_custom_command(TARGET ${CONTEXT} PRE_LINK COMMAND ${CPP} ARGS ${CPP_ARGS_LD_SCRIPT} BYPRODUCTS ${LD_SCRIPT_PP})"
} else {
linkerVars += "\nset(LD_SCRIPT_PP ${LD_SCRIPT})"
}
return linkerVars, linkerOptions
}
func (c *Cbuild) AdjustRelativePath(option string) string {
pattern := regexp.MustCompile(`\./.*|\.\./.*`)
if pattern.MatchString(option) {
relativePath := pattern.FindString(option)
option = strings.Replace(option, relativePath, AddRootPrefix(c.ContextRoot, relativePath), 1)
}
return option
}
func QuoteArguments(cmd string) string {
pattern := regexp.MustCompile(`(\${INPUT(_\d)?}|\${OUTPUT(_\d)?})`)
return pattern.ReplaceAllString(cmd, "\"${1}\"")
}
func ListExecutesIOs(io string, list []string, run string) string {
content := "\nset(" + io
var listItems string
for index, input := range list {
content += "\n " + AddRootPrefix("", input)
if strings.Contains(run, "${"+io+"_"+strconv.Itoa(index)+"}") {
listItems += "\nlist(GET " + io + " " + strconv.Itoa(index) + " " + io + "_" + strconv.Itoa(index) + ")"
}
}
content += "\n)"
content += listItems
return content
}
func ExecutesCommands(executes []Executes) string {
var content string
for _, item := range executes {
content += "\n\n# Execute: " + item.Execute
customTarget := "\nadd_custom_target(" + item.Execute + " ALL"
runAlways := item.Always != nil
if runAlways {
customTarget += "\n COMMAND " + QuoteArguments(item.Run)
customTarget += "\n COMMENT " + item.Execute + "\n)"
} else {
customTarget += " DEPENDS ${OUTPUT})"
}
customCommand := "\nadd_custom_command(OUTPUT ${OUTPUT}"
if len(item.Input) > 0 {
content += ListExecutesIOs("INPUT", item.Input, item.Run)
customCommand += " DEPENDS ${INPUT}"
}
if !runAlways && len(item.Output) == 0 {
item.Output = append(item.Output, "${CMAKE_CURRENT_BINARY_DIR}/"+item.Execute+".stamp")
customCommand += "\n COMMAND ${CMAKE_COMMAND} -E touch \"" + item.Execute + ".stamp\""
}
if len(item.Output) > 0 {
content += ListExecutesIOs("OUTPUT", item.Output, item.Run)
}
content += customTarget
if !runAlways {
customCommand += "\n COMMAND " + QuoteArguments(item.Run)
customCommand += "\n COMMENT " + item.Execute + "\n)"
content += customCommand
}
}
return content
}