Skip to content

Commit 6e964a6

Browse files
authored
Merge pull request #5476 from ubirch/add-cmake-export
add cmake exporter for CLion use
2 parents da6532e + 0a40a52 commit 6e964a6

File tree

4 files changed

+374
-1
lines changed

4 files changed

+374
-1
lines changed

tools/export/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from tools.export import sw4stm32, e2studio, zip, cmsis, uvision, cdt, vscode
3434
from tools.export import gnuarmeclipse
3535
from tools.export import qtcreator
36+
from tools.export import cmake
3637
from tools.export import nb
3738
from tools.targets import TARGET_NAMES
3839

@@ -63,7 +64,8 @@
6364
'qtcreator': qtcreator.QtCreator,
6465
'vscode_gcc_arm' : vscode.VSCodeGcc,
6566
'vscode_iar' : vscode.VSCodeIAR,
66-
'vscode_armc5' : vscode.VSCodeArmc5
67+
'vscode_armc5' : vscode.VSCodeArmc5,
68+
'cmake_gcc_arm': cmake.GccArm
6769
}
6870

6971
ERROR_MESSAGE_UNSUPPORTED_TOOLCHAIN = """
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# This file was automagically generated by mbed.org.
2+
# If you would like to add your own targets, create a
3+
# project.cmake file locally in your project directory.
4+
5+
CMAKE_MINIMUM_REQUIRED(VERSION 3.9)
6+
SET(CMAKE_SYSTEM_NAME Generic)
7+
#SET(CMAKE_SYSTEM_PROCESSOR arm)
8+
SET(CMAKE_CROSSCOMPILING TRUE)
9+
10+
# force compiler settings
11+
SET(CMAKE_C_COMPILER_WORKS TRUE)
12+
SET(CMAKE_CXX_COMPILER_WORKS TRUE)
13+
14+
# force cmake compilers
15+
SET(CMAKE_ASM_COMPILER "{{asm}}")
16+
SET(CMAKE_C_COMPILER "{{cc}}")
17+
SET(CMAKE_CXX_COMPILER "{{cxx}}")
18+
SET(ELF2BIN "{{elf2bin}}")
19+
{% if hex_files %}
20+
SET(SREC_CAT "srec_cat")
21+
{%- endif %}
22+
23+
# if the environment does not specify build type, set to Debug
24+
IF(NOT CMAKE_BUILD_TYPE)
25+
set(CMAKE_BUILD_TYPE "Debug"
26+
CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel."
27+
FORCE)
28+
ENDIF()
29+
30+
# here starts the project
31+
PROJECT({{name}} C CXX ASM)
32+
33+
# uncomment below to have a verbose build process
34+
#SET(CMAKE_VERBOSE_MAKEFILE ON)
35+
36+
SET(LD_SYS_LIBS "{%- block sys_libs -%} -Wl,--start-group {{ld_sys_libs|join(" ")}} {{libraries|join(" ")}} -Wl,--end-group {%- endblock -%}")
37+
38+
SET(CMAKE_C_FLAGS "{{cc_flags}} -include mbed_config.h")
39+
SET(CMAKE_CXX_FLAGS "{{cxx_flags}} -include mbed_config.h")
40+
SET(CMAKE_ASM_FLAGS "{{asm_flags}} -include mbed_config.h")
41+
SET(CMAKE_CXX_LINK_FLAGS "{{ld_flags}} {% for p in library_paths %} {{user_library_flag}}${CMAKE_CURRENT_SOURCE_DIR}/{{p}} {% endfor %}")
42+
{% if pp -%}
43+
SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} ${LD_SYS_LIBS} {{link_script_option}} ${CMAKE_BINARY_DIR}/{{name}}_pp.link_script.ld")
44+
{%- endif %}
45+
46+
ADD_DEFINITIONS(
47+
{% for d in symbols %}-D{{d}}
48+
{% endfor %})
49+
INCLUDE_DIRECTORIES(
50+
{% for p in include_paths %}{{p}}
51+
{% endfor %})
52+
53+
# executable {{name}}
54+
ADD_EXECUTABLE({{name}}
55+
{% for src in sources %}{{src}}
56+
{% endfor %})
57+
SET_TARGET_PROPERTIES({{name}} PROPERTIES ENABLE_EXPORTS 1)
58+
# add syslibs dependencies to create the correct linker order
59+
TARGET_LINK_LIBRARIES({{name}} {{ld_sys_libs|join(" ")}})
60+
61+
{% if pp -%}
62+
add_custom_command(TARGET {{name}} PRE_LINK
63+
COMMAND "{{pp}}" {{pp_flags}} {{linker_script}} -o ${CMAKE_CURRENT_BINARY_DIR}/{{name}}_pp.link_script.ld
64+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
65+
BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/{{name}}_pp.link_script.ld"
66+
)
67+
{%- endif %}
68+
69+
add_custom_command(TARGET {{name}} POST_BUILD
70+
COMMAND ${ELF2BIN} -O ihex $<TARGET_FILE:{{name}}> $<TARGET_FILE:{{name}}>.hex
71+
COMMAND ${CMAKE_COMMAND} -E echo "-- built: $<TARGET_FILE:{{name}}>.hex"
72+
)
73+
74+
75+
##########################################################################
76+
# mbed-cli specific targets
77+
##########################################################################
78+
79+
# detect the build type and select the corresponding cli profile
80+
SET(MBED_BUILD_PROFILE "")
81+
STRING(TOLOWER ${CMAKE_BUILD_TYPE} LOWERCASE_CMAKE_BUILD_TYPE)
82+
IF(LOWERCASE_CMAKE_BUILD_TYPE MATCHES debug)
83+
SET(MBED_BUILD_PROFILE "mbed-os/tools/profiles/debug.json")
84+
ELSEIF(LOWERCASE_CMAKE_BUILD_TYPE MATCHES relwithdebinfo)
85+
SET(MBED_BUILD_PROFILE "mbed-os/tools/profiles/develop.json")
86+
ELSEIF(LOWERCASE_CMAKE_BUILD_TYPE MATCHES release)
87+
SET(MBED_BUILD_PROFILE "mbed-os/tools/profiles/release.json")
88+
ELSEIF(LOWERCASE_CMAKE_BUILD_TYPE MATCHES minsizerel)
89+
SET(MBED_BUILD_PROFILE "mbed-os/tools/profiles/release.json")
90+
ELSE()
91+
MESSAGE(WARNING "Build type '${CMAKE_BUILD_TYPE}' is unknown, using debug profile")
92+
SET(MBED_BUILD_PROFILE "mbed-os/tools/profiles/debug.json")
93+
ENDIF()
94+
95+
# optional custom target to build via mbed-cli
96+
ADD_CUSTOM_TARGET(mbed-cli-build
97+
COMMAND ${CMAKE_COMMAND} -E echo "mbed compile --build BUILD/${CMAKE_BUILD_TYPE} --profile ${MBED_BUILD_PROFILE}"
98+
COMMAND mbed compile --build BUILD/${CMAKE_BUILD_TYPE} --profile ${MBED_BUILD_PROFILE}
99+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
100+
SOURCES ${SOURCE_FILES} ${SYS_SOURCE_FILES})
101+
102+
IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/project.cmake)
103+
INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/project.cmake)
104+
ELSE()
105+
MESSAGE(STATUS "Add a local project.cmake file to add your own targets.")
106+
ENDIF()

tools/export/cmake/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# CLion CMakeLists.txt Generator
2+
3+
This exporter generates a CMakeLists.txt file that can be used to
4+
develop mbed using [IntelliJ CLion](https://www.jetbrains.com/clion/).
5+
6+
It will not create a functional CMake build system that mimics the
7+
mbed build system, but rather uses the mbed-cli itself to compile
8+
the targets. The generated files help CLion to understand the
9+
includes and dependencies of your code.
10+
11+
Run the following command to create/overwrite your CMakeLists.txt.
12+
```
13+
mbed export -i cmake_gcc_arm
14+
```
15+
> Run the command again if files or libraries have been added or removed.

tools/export/cmake/__init__.py

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
"""
2+
mbed SDK
3+
Copyright (c) 2011-2016 ARM Limited
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
"""
17+
import re
18+
import shutil
19+
from os import remove, getcwd, chdir, mkdir
20+
from os.path import basename, exists
21+
from subprocess import Popen, PIPE
22+
23+
from jinja2.exceptions import TemplateNotFound
24+
25+
from tools.export.exporters import Exporter, apply_supported_whitelist
26+
from tools.targets import TARGET_MAP
27+
28+
29+
class CMake(Exporter):
30+
"""Generic CMake template that mimics the behavior of the python build
31+
system
32+
"""
33+
34+
TEMPLATE = 'CMakeLists.txt'
35+
36+
MBED_CONFIG_HEADER_SUPPORTED = True
37+
38+
PREPROCESS_ASM = False
39+
40+
POST_BINARY_WHITELIST = set([
41+
"MCU_NRF51Code.binary_hook",
42+
"TEENSY3_1Code.binary_hook",
43+
"LPCTargetCode.lpc_patch",
44+
"LPC4088Code.binary_hook"
45+
])
46+
47+
@classmethod
48+
def is_target_supported(cls, target_name):
49+
target = TARGET_MAP[target_name]
50+
return apply_supported_whitelist(
51+
cls.TOOLCHAIN, cls.POST_BINARY_WHITELIST, target)
52+
53+
def generate(self):
54+
"""Generate the CMakefiles.txt
55+
"""
56+
self.resources.win_to_unix()
57+
58+
# get all source files including headers, adding headers allows IDEs to detect which files
59+
# belong to the project, otherwise headers may be greyed out and not work with inspection
60+
# (that is true for CLion and definitely for Visual Code)
61+
srcs = set(self.resources.c_sources +
62+
self.resources.cpp_sources +
63+
self.resources.s_sources +
64+
self.resources.headers)
65+
srcs = [re.sub(r'^[.]/', '', f) for f in srcs]
66+
67+
# additional libraries
68+
libraries = [self.prepare_lib(basename(lib)) for lib in self.resources.libraries]
69+
sys_libs = [self.prepare_sys_lib(lib) for lib in self.toolchain.sys_libs]
70+
71+
# sort includes reverse, so the deepest dir comes first (ensures short includes)
72+
includes = sorted([re.sub(r'^[.]/', '', l) for l in self.resources.inc_dirs], reverse=True)
73+
74+
ctx = {
75+
'name': self.project_name,
76+
'target': self.target,
77+
'sources': sorted(srcs),
78+
'libraries': libraries,
79+
'ld_sys_libs': sys_libs,
80+
'include_paths': includes,
81+
'library_paths': sorted([re.sub(r'^[.]/', '', l) for l in self.resources.lib_dirs]),
82+
'linker_script': self.resources.linker_script,
83+
'hex_files': self.resources.hex_files,
84+
'ar': basename(self.toolchain.ar),
85+
'cc': basename(self.toolchain.cc[0]),
86+
'cc_flags': " ".join(flag for flag in self.toolchain.cc[1:] if not flag == "-c"),
87+
'cxx': basename(self.toolchain.cppc[0]),
88+
'cxx_flags': " ".join(flag for flag in self.toolchain.cppc[1:] if not flag == "-c"),
89+
'asm': basename(self.toolchain.asm[0]),
90+
'asm_flags': " ".join(flag for flag in self.toolchain.asm[1:] if not flag == "-c"),
91+
'symbols': sorted(self.toolchain.get_symbols()),
92+
'ld': basename(self.toolchain.ld[0]),
93+
# fix the missing underscore '_' (see
94+
'ld_flags': re.sub("--wrap,_(?!_)", "--wrap,__", " ".join(self.toolchain.ld[1:])),
95+
'elf2bin': basename(self.toolchain.elf2bin),
96+
'link_script_ext': self.toolchain.LINKER_EXT,
97+
'link_script_option': self.LINK_SCRIPT_OPTION,
98+
'user_library_flag': self.USER_LIBRARY_FLAG,
99+
'needs_asm_preproc': self.PREPROCESS_ASM,
100+
}
101+
102+
if hasattr(self.toolchain, "preproc"):
103+
ctx['pp'] = basename(self.toolchain.preproc[0])
104+
ctx['pp_flags'] = " ".join(self.toolchain.preproc[1:] +
105+
self.toolchain.ld[1:])
106+
else:
107+
ctx['pp'] = None
108+
ctx['pp_flags'] = None
109+
110+
try:
111+
self.gen_file('cmake/%s.tmpl' % self.TEMPLATE, ctx, 'CMakeLists.txt')
112+
except TemplateNotFound:
113+
pass
114+
115+
@staticmethod
116+
def build(project_name, log_name="build_log.txt", cleanup=True):
117+
""" Build Make project """
118+
119+
# change into our build directory
120+
current_dir = getcwd()
121+
if not exists("BUILD"):
122+
mkdir("BUILD")
123+
chdir("BUILD")
124+
125+
# > run cmake initial command
126+
cmd = ["cmake", ".."]
127+
128+
# Build the project
129+
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
130+
out, err = p.communicate()
131+
ret_code = p.returncode
132+
133+
if ret_code == 0:
134+
# we create the cmake files inside BUILD, change into and run cmake
135+
136+
# > run make -j
137+
cmd = ["make", "-j"]
138+
139+
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
140+
out, err = p.communicate()
141+
ret_code = p.returncode
142+
143+
# go back to the original directory
144+
chdir(current_dir)
145+
146+
out_string = "=" * 10 + "STDOUT" + "=" * 10 + "\n"
147+
out_string += out
148+
out_string += "=" * 10 + "STDERR" + "=" * 10 + "\n"
149+
out_string += err
150+
151+
if ret_code == 0:
152+
out_string += "SUCCESS"
153+
else:
154+
out_string += "FAILURE"
155+
156+
print out_string
157+
158+
if log_name:
159+
# Write the output to the log file
160+
with open(log_name, 'w+') as f:
161+
f.write(out_string)
162+
163+
# Cleanup the exported and built files
164+
if cleanup:
165+
remove("CMakeLists.txt")
166+
remove(log_name)
167+
# legacy .build directory cleaned if exists
168+
if exists('.build'):
169+
shutil.rmtree('.build')
170+
if exists('BUILD'):
171+
shutil.rmtree('BUILD')
172+
173+
if ret_code != 0:
174+
# Seems like something went wrong.
175+
return -1
176+
else:
177+
return 0
178+
179+
180+
class GccArm(CMake):
181+
"""GCC ARM specific cmake target"""
182+
NAME = 'CMake-GCC-ARM'
183+
TOOLCHAIN = "GCC_ARM"
184+
LINK_SCRIPT_OPTION = "-T"
185+
USER_LIBRARY_FLAG = "-L"
186+
187+
@staticmethod
188+
def prepare_lib(libname):
189+
if "lib" == libname[:3]:
190+
libname = libname[3:-2]
191+
return "-l" + libname
192+
193+
@staticmethod
194+
def prepare_sys_lib(libname):
195+
return "-l" + libname
196+
197+
# class Arm(CMake):
198+
# """ARM Compiler generic cmake target"""
199+
# LINK_SCRIPT_OPTION = "--scatter"
200+
# USER_LIBRARY_FLAG = "--userlibpath "
201+
#
202+
# @staticmethod
203+
# def prepare_lib(libname):
204+
# return libname
205+
#
206+
# @staticmethod
207+
# def prepare_sys_lib(libname):
208+
# return libname
209+
#
210+
# def generate(self):
211+
# if self.resources.linker_script:
212+
# new_script = self.toolchain.correct_scatter_shebang(
213+
# self.resources.linker_script)
214+
# if new_script is not self.resources.linker_script:
215+
# self.resources.linker_script = new_script
216+
# self.generated_files.append(new_script)
217+
# return super(Arm, self).generate()
218+
#
219+
#
220+
# class Armc5(Arm):
221+
# """ARM Compiler 5 (armcc) specific makefile target"""
222+
# NAME = 'CMake-ARMc5'
223+
# TOOLCHAIN = "ARM"
224+
# PREPROCESS_ASM = True
225+
#
226+
#
227+
# class Armc6(Arm):
228+
# """ARM Compiler 6 (armclang) specific generic makefile target"""
229+
# NAME = 'CMake-ARMc6'
230+
# TOOLCHAIN = "ARMC6"
231+
#
232+
#
233+
# class IAR(CMake):
234+
# """IAR specific cmake target"""
235+
# NAME = 'CMake-IAR'
236+
# TOOLCHAIN = "IAR"
237+
# LINK_SCRIPT_OPTION = "--config"
238+
# USER_LIBRARY_FLAG = "-L"
239+
#
240+
# @staticmethod
241+
# def prepare_lib(libname):
242+
# if "lib" == libname[:3]:
243+
# libname = libname[3:]
244+
# return "-l" + splitext(libname)[0]
245+
#
246+
# @staticmethod
247+
# def prepare_sys_lib(libname):
248+
# if "lib" == libname[:3]:
249+
# libname = libname[3:]
250+
# return "-l" + splitext(libname)[0]

0 commit comments

Comments
 (0)