Skip to content

Commit eda766e

Browse files
author
Jamie Smith
authored
Refactor upload methods to streamline the debugging experience (ARMmbed#129)
* Change all debug configurations to not reset and exit on error. Initial tasks.json implementation. * st-util working on command line and VS Code! * Convert STM32CUBE, fix spaces in GDB server task command * pyocd working! * Convert OpenOCD * Convert JLINK * Convert REDLINK upload method * Fix CMake typo * Try and fix some variable scope issues * Initial CLion implementation. Need to test with hardware. * Add a separate task that kicks off a build before each debug session
1 parent 141aaec commit eda766e

9 files changed

+353
-99
lines changed

tools/cmake/UploadMethodManager.cmake

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ set(UPLOAD_METHOD "${UPLOAD_METHOD_DEFAULT}" CACHE STRING "Method for uploading
1414
# use a higher numbered port to allow use without root on Linux/Mac
1515
set(GDB_PORT 23331 CACHE STRING "Port that the GDB server will be started on.")
1616

17+
# Load the upload method. This is expected to set the following variables:
18+
# UPLOAD_${UPLOAD_METHOD}_FOUND - True iff the dependencies for this upload method were found
19+
# UPLOAD_SUPPORTS_DEBUG - True iff this upload method supports debugging
20+
# UPLOAD_GDBSERVER_DEBUG_COMMAND - Command to start a new GDB server
21+
# UPLOAD_WANTS_EXTENDED_REMOTE - True iff GDB should use "target extended-remote" to connect to the GDB server
22+
# UPLOAD_LAUNCH_COMMANDS - List of GDB commands to run after launching GDB.
23+
# UPLOAD_RESTART_COMMANDS - List of commands to run when the "restart chip" function is used.
24+
# See here for more info: https://github.com/mbed-ce/mbed-os/wiki/Debugger-Commands-and-State-in-Upload-Methods
1725
include(UploadMethod${UPLOAD_METHOD})
1826

1927
if(NOT "${UPLOAD_${UPLOAD_METHOD}_FOUND}")
@@ -24,46 +32,27 @@ if(NOT (("${UPLOAD_METHOD}" STREQUAL NONE) OR ("${${UPLOAD_METHOD}_UPLOAD_ENABLE
2432
message(FATAL_ERROR "The upload method ${UPLOAD_METHOD} is not enabled in the config code for this target -- set ${UPLOAD_METHOD}_UPLOAD_ENABLED to TRUE to enable it.")
2533
endif()
2634

27-
message(STATUS "Board upload method set to ${UPLOAD_METHOD}")
28-
29-
# ----------------------------------------------
30-
# Generate gdbinit if needed
31-
3235
if(UPLOAD_SUPPORTS_DEBUG)
33-
# create init file for GDB client
34-
if(UPLOAD_WANTS_EXTENDED_REMOTE)
35-
set(UPLOAD_GDB_REMOTE_KEYWORD "extended-remote")
36-
else()
37-
set(UPLOAD_GDB_REMOTE_KEYWORD "remote")
38-
endif()
39-
40-
file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/mbed-cmake.gdbinit CONTENT
41-
"# connect to GDB server
42-
target ${UPLOAD_GDB_REMOTE_KEYWORD} localhost:${GDB_PORT}
43-
")
36+
message(STATUS "Mbed: Code upload and debugging enabled via upload method ${UPLOAD_METHOD}")
37+
elseif(NOT "${UPLOAD_METHOD}" STREQUAL "NONE")
38+
message(STATUS "Mbed: Code upload enabled via upload method ${UPLOAD_METHOD}")
4439
endif()
4540

46-
# UPLOAD_SUPPORTS_DEBUG needs to be made into a cache variable so that it can
41+
# These variables need to be made into a cache variables so that they can
4742
# be seen by higher level directories when they call mbed_generate_upload_debug_targets()
4843
set(MBED_UPLOAD_SUPPORTS_DEBUG ${UPLOAD_SUPPORTS_DEBUG} CACHE INTERNAL "" FORCE)
44+
set(MBED_UPLOAD_GDBSERVER_DEBUG_COMMAND ${UPLOAD_GDBSERVER_DEBUG_COMMAND} CACHE INTERNAL "" FORCE)
45+
set(MBED_UPLOAD_WANTS_EXTENDED_REMOTE ${UPLOAD_WANTS_EXTENDED_REMOTE} CACHE INTERNAL "" FORCE)
46+
set(MBED_UPLOAD_LAUNCH_COMMANDS ${UPLOAD_LAUNCH_COMMANDS} CACHE INTERNAL "" FORCE)
47+
set(MBED_UPLOAD_RESTART_COMMANDS ${UPLOAD_RESTART_COMMANDS} CACHE INTERNAL "" FORCE)
4948

5049
# ----------------------------------------------
5150
# Function for creating targets
5251

53-
function(mbed_generate_upload_debug_targets target)
52+
function(mbed_generate_upload_target target)
5453
# add upload target
5554
gen_upload_target(${target}
5655
${CMAKE_CURRENT_BINARY_DIR}/$<TARGET_FILE_BASE_NAME:${target}>.bin
5756
${CMAKE_CURRENT_BINARY_DIR}/$<TARGET_FILE_BASE_NAME:${target}>.hex
5857
)
59-
60-
# add debug target
61-
if(MBED_UPLOAD_SUPPORTS_DEBUG AND MBED_GDB_FOUND)
62-
add_custom_target(debug-${target}
63-
COMMENT "starting GDB to debug ${target}..."
64-
COMMAND ${MBED_GDB}
65-
--command=${CMAKE_BINARY_DIR}/mbed-cmake.gdbinit
66-
$<TARGET_FILE:${target}>
67-
USES_TERMINAL)
68-
endif()
6958
endfunction()

tools/cmake/mbed_ide_debug_cfg_generator.cmake

Lines changed: 186 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,21 @@
77
# -------------------------------------------------------------
88

99
if($ENV{CLION_IDE})
10-
message(STATUS "Detected CLion IDE, will generate CLion debug configurations")
10+
message(STATUS "Mbed: Detected CLion IDE, will generate CLion debug configurations")
1111
set(MBED_GENERATE_CLION_DEBUG_CFGS TRUE)
1212

13+
set(MBED_CLION_PROFILE_NAME "" CACHE STRING "Name of the Clion build profile (Settings > Build, Execution, Deployment > CMake > Name textbox")
14+
15+
if(MBED_CLION_PROFILE_NAME STREQUAL "")
16+
message(FATAL_ERROR "In order to generate CLion configuration files, Mbed CE needs to know the name of the current CLion build profile. This name is the string typed into the Name textbox under Settings > Build, Execution, Deployment > CMake. Pass this name with '-DMBED_CLION_PROFILE_NAME=<name>'.")
17+
endif()
18+
1319
elseif(CMAKE_EXPORT_COMPILE_COMMANDS) # TODO: Is this actually a reliable way of detecting VS Code? Not sure if it will create false positives.
14-
message(STATUS "Detected VS Code IDE, will generate VS Code debug configurations")
20+
message(STATUS "Mbed: Detected VS Code IDE, will generate VS Code debug configurations")
1521
set(MBED_GENERATE_VS_CODE_DEBUG_CFGS TRUE)
1622

23+
elseif(MBED_UPLOAD_SUPPORTS_DEBUG)
24+
message(STATUS "Mbed: No IDE detected, will generate configurations for command-line debugging (e.g. ninja gdbserver, then ninja debug-SomeProgram)")
1725
endif()
1826

1927
# CLion generator
@@ -31,11 +39,43 @@ if(MBED_GENERATE_CLION_DEBUG_CFGS)
3139
set(CONFIG_NAME "GDB ${CMAKE_TARGET} ${MBED_TARGET} ${CMAKE_BUILD_TYPE}")
3240
set(RUN_CONF_PATH "${CLION_RUN_CONF_DIR}/${CONFIG_NAME}.xml")
3341

42+
# Convert the CMake list into the correct format for the run configuration XML
43+
list(GET MBED_UPLOAD_GDBSERVER_DEBUG_COMMAND 0 GDBSERVER_EXECUTABLE)
44+
list(SUBLIST MBED_UPLOAD_GDBSERVER_DEBUG_COMMAND 1 -1 GDBSERVER_ARGS)
45+
set(GDBSERVER_ARGS_STR "")
46+
set(IS_FIRST_ARG TRUE)
47+
foreach(ELEMENT ${GDBSERVER_ARGS})
48+
49+
if(IS_FIRST_ARG)
50+
set(IS_FIRST_ARG FALSE)
51+
else()
52+
string(APPEND GDBSERVER_ARGS_STR " ")
53+
endif()
54+
55+
# Escape quotes and ampersands
56+
string(REPLACE "\"" "&quot;" ELEMENT "${ELEMENT}")
57+
string(REPLACE "&" "&amp;" ELEMENT "${ELEMENT}")
58+
59+
if("${ELEMENT}" MATCHES " ")
60+
string(APPEND GDBSERVER_ARGS_STR "&quot;${ELEMENT}&quot;")
61+
else()
62+
string(APPEND GDBSERVER_ARGS_STR "${ELEMENT}")
63+
endif()
64+
endforeach()
65+
66+
# Generate run configuration XML file.
67+
# This file is based on a generic Embedded GDB Server run configuration generated by CLion,
68+
# with constant strings replaced by placeholders for CMake.
3469
file(GENERATE OUTPUT ${RUN_CONF_PATH} CONTENT
3570
"<!-- Autogenerated by Mbed OS. Do not edit! -->
3671
<component name=\"ProjectRunConfigurationManager\">
37-
<configuration default=\"false\" name=\"${CONFIG_NAME}\" type=\"CLion_Remote\" remoteCommand=\"localhost:${GDB_PORT}\" symbolFile=\"$<TARGET_FILE:${CMAKE_TARGET}>\" sysroot=\"\">
38-
<method v=\"2\" />
72+
<configuration default=\"false\" name=\"${CONFIG_NAME}\" type=\"com.jetbrains.cidr.embedded.customgdbserver.type\" PROGRAM_PARAMS=\"${GDBSERVER_ARGS_STR}\" REDIRECT_INPUT=\"false\" ELEVATE=\"false\" USE_EXTERNAL_CONSOLE=\"false\" PASS_PARENT_ENVS_2=\"true\" PROJECT_NAME=\"${PROJECT_NAME}\" TARGET_NAME=\"${CMAKE_TARGET}\" CONFIG_NAME=\"${MBED_CLION_PROFILE_NAME}\" version=\"1\" RUN_TARGET_PROJECT_NAME=\"${PROJECT_NAME}\" RUN_TARGET_NAME=\"${CMAKE_TARGET}\">
73+
<custom-gdb-server version=\"1\" gdb-connect=\"localhost:${GDB_PORT}\" executable=\"${GDBSERVER_EXECUTABLE}\" warmup-ms=\"0\" download-type=\"UPDATED_ONLY\" reset-cmd=\"monitor reset\" reset-type=\"AFTER_DOWNLOAD\">
74+
<debugger kind=\"GDB\" isBundled=\"true\" />
75+
</custom-gdb-server>
76+
<method v=\"2\">
77+
<option name=\"CLION.COMPOUND.BUILD\" enabled=\"true\" />
78+
</method>
3979
</configuration>
4080
</component>
4181
")
@@ -55,9 +95,16 @@ elseif(MBED_GENERATE_VS_CODE_DEBUG_CFGS)
5595
# Start building up json file. Needs to be a global property so we can append to it from anywhere.
5696
# Note: Cannot use a cache variable for this because cache variables aren't allowed to contain newlines.
5797
set_property(GLOBAL PROPERTY VSCODE_LAUNCH_JSON_CONTENT
58-
"{
98+
"// Auto-generated by Mbed CE. Edits will be erased when CMake is rerun.
99+
{
59100
\"configurations\": [")
60101

102+
set_property(GLOBAL PROPERTY VSCODE_TASKS_JSON_CONTENT
103+
"// Auto-generated by Mbed CE. Edits will be erased when CMake is rerun.
104+
{
105+
\"version\": \"2.0.0\",
106+
\"tasks\": [")
107+
61108
# Find objdump as the extension uses it. In a sane world it should be in the compiler bin dir.
62109
find_program(MBED_OBJDUMP
63110
NAMES arm-none-eabi-objdump objdump
@@ -68,10 +115,15 @@ elseif(MBED_GENERATE_VS_CODE_DEBUG_CFGS)
68115
function(mbed_generate_ide_debug_configuration CMAKE_TARGET)
69116

70117
# Create name (combine target name, Mbed target, and build config to generate a unique string)
71-
set(CONFIG_NAME "Connect to GDB ${CMAKE_TARGET} ${MBED_TARGET} ${CMAKE_BUILD_TYPE}")
118+
set(CONFIG_NAME "Debug ${CMAKE_TARGET} ${MBED_TARGET} ${CMAKE_BUILD_TYPE}")
119+
120+
# Convert CMake lists to json
121+
list(JOIN MBED_UPLOAD_LAUNCH_COMMANDS "\", \"" UPLOAD_LAUNCH_COMMANDS_FOR_JSON)
122+
list(JOIN MBED_UPLOAD_RESTART_COMMANDS "\", \"" UPLOAD_RESTART_COMMANDS_FOR_JSON)
72123

73124
# property list here: https://github.com/Marus/cortex-debug/blob/master/debug_attributes.md
74125
set_property(GLOBAL APPEND_STRING PROPERTY VSCODE_LAUNCH_JSON_CONTENT "
126+
// Debug launch for target ${CMAKE_TARGET}.
75127
{
76128
\"type\": \"cortex-debug\",
77129
\"name\": \"${CONFIG_NAME}\",
@@ -81,32 +133,155 @@ elseif(MBED_GENERATE_VS_CODE_DEBUG_CFGS)
81133
\"objdumpPath\": \"${MBED_OBJDUMP}\",
82134
\"servertype\": \"external\",
83135
\"gdbTarget\": \"localhost:${GDB_PORT}\",
84-
\"request\": \"attach\"
136+
\"request\": \"launch\",
137+
\"preLaunchTask\": \"Build ${CMAKE_TARGET} and start GDB server\",
138+
// Override the command sequences used by VS Code to be correct for this GDB server
139+
\"overrideLaunchCommands\": [\"${UPLOAD_LAUNCH_COMMANDS_FOR_JSON}\"],
140+
\"overrideRestartCommands\": [\"${UPLOAD_RESTART_COMMANDS_FOR_JSON}\"],
85141
},")
86142

143+
# Add tasks to both build only, and build and start the GDB server.
144+
# Schema for tasks.json can be seen here https://code.visualstudio.com/docs/editor/tasks-appendix
145+
set_property(GLOBAL APPEND_STRING PROPERTY VSCODE_TASKS_JSON_CONTENT "
146+
// Build for target ${CMAKE_TARGET}
147+
{
148+
\"label\": \"Build ${CMAKE_TARGET}\",
149+
\"type\": \"shell\",
150+
\"command\": \"${CMAKE_COMMAND}\",
151+
\"args\": [\"--build\", \"${CMAKE_BINARY_DIR}\", \"--target\", \"${CMAKE_TARGET}\"],
152+
},
153+
// Build ${CMAKE_TARGET} and run the GDB server
154+
{
155+
\"label\": \"Build ${CMAKE_TARGET} and start GDB server\",
156+
\"dependsOn\": [\"Build ${CMAKE_TARGET}\", \"GDB Server\"],
157+
\"dependsOrder\": \"sequence\",
158+
},")
159+
87160
endfunction(mbed_generate_ide_debug_configuration)
88161

89162
# Take all generated debug configurations and write them to launch.json.
90163
function(mbed_finalize_ide_debug_configurations)
164+
165+
91166
# Add footer
92167
set_property(GLOBAL APPEND_STRING PROPERTY VSCODE_LAUNCH_JSON_CONTENT "
93168
]
94169
}")
95170

96171
get_property(VSCODE_LAUNCH_JSON_CONTENT GLOBAL PROPERTY VSCODE_LAUNCH_JSON_CONTENT)
97172
file(GENERATE OUTPUT ${VSCODE_LAUNCH_JSON_PATH} CONTENT ${VSCODE_LAUNCH_JSON_CONTENT})
173+
174+
175+
# Convert the CMake list into the correct format for tasks.json
176+
list(GET MBED_UPLOAD_GDBSERVER_DEBUG_COMMAND 0 GDBSERVER_EXECUTABLE)
177+
list(SUBLIST MBED_UPLOAD_GDBSERVER_DEBUG_COMMAND 1 -1 GDBSERVER_ARGS)
178+
set(GDBSERVER_ARGS_STR "")
179+
set(IS_FIRST_ARG TRUE)
180+
foreach(ELEMENT ${GDBSERVER_ARGS})
181+
182+
if(IS_FIRST_ARG)
183+
set(IS_FIRST_ARG FALSE)
184+
else()
185+
string(APPEND GDBSERVER_ARGS_STR ", ")
186+
endif()
187+
188+
# Escape any quotes in the element
189+
string(REPLACE "\"" "\\\"" ELEMENT "${ELEMENT}")
190+
191+
string(APPEND GDBSERVER_ARGS_STR "\"${ELEMENT}\"")
192+
endforeach()
193+
194+
set_property(GLOBAL APPEND_STRING PROPERTY VSCODE_TASKS_JSON_CONTENT "
195+
{
196+
\"label\": \"GDB Server\",
197+
\"type\": \"shell\",
198+
\"command\": \"${GDBSERVER_EXECUTABLE}\",
199+
\"args\": [${GDBSERVER_ARGS_STR}],
200+
\"isBackground\": true,
201+
// This task is run to start the GDB server, so that the launch configuration can connect to it.
202+
// Problem is, it's a GDB server, and since it never exits, VSCode
203+
// will never start the debug session. All this is needed so VSCode just lets it run.
204+
\"problemMatcher\": [
205+
{
206+
\"pattern\": [
207+
{
208+
\"regexp\": \"________________\",
209+
\"file\": 1,
210+
\"location\": 2,
211+
\"message\": 3
212+
}
213+
],
214+
\"background\": {
215+
\"activeOnStart\": true,
216+
\"beginsPattern\": \".*\",
217+
\"endsPattern\": \".*\",
218+
}
219+
}
220+
],
221+
}
222+
]
223+
224+
}
225+
")
226+
227+
# Write out tasks.json
228+
set(VSCODE_TASKS_JSON_PATH ${CMAKE_SOURCE_DIR}/.vscode/tasks.json)
229+
get_property(VSCODE_TASKS_JSON_CONTENT GLOBAL PROPERTY VSCODE_TASKS_JSON_CONTENT)
230+
file(GENERATE OUTPUT ${VSCODE_TASKS_JSON_PATH} CONTENT ${VSCODE_TASKS_JSON_CONTENT})
231+
98232
endfunction(mbed_finalize_ide_debug_configurations)
99233

100-
# No-op generator
234+
# Command-line generator
101235
# -------------------------------------------------------------
102-
else()
236+
elseif(MBED_UPLOAD_SUPPORTS_DEBUG)
103237

104238
function(mbed_generate_ide_debug_configuration CMAKE_TARGET)
105-
# Empty
239+
240+
# add debug target
241+
if(MBED_UPLOAD_SUPPORTS_DEBUG AND MBED_GDB_FOUND)
242+
add_custom_target(debug-${target}
243+
COMMENT "Starting GDB to debug ${target}..."
244+
COMMAND ${MBED_GDB}
245+
--command=${CMAKE_BINARY_DIR}/mbed-cmake.gdbinit
246+
$<TARGET_FILE:${target}>
247+
USES_TERMINAL)
248+
endif()
249+
106250
endfunction(mbed_generate_ide_debug_configuration)
107251

108252
function(mbed_finalize_ide_debug_configurations)
109-
# Empty
253+
254+
# create init file for GDB client
255+
if(MBED_UPLOAD_WANTS_EXTENDED_REMOTE)
256+
set(UPLOAD_GDB_REMOTE_KEYWORD "extended-remote")
257+
else()
258+
set(UPLOAD_GDB_REMOTE_KEYWORD "remote")
259+
endif()
260+
261+
list(JOIN MBED_UPLOAD_LAUNCH_COMMANDS "\n" MBED_UPLOAD_LAUNCH_COMMANDS_FOR_GDBINIT)
262+
263+
file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/mbed-cmake.gdbinit CONTENT
264+
"# connect to GDB server
265+
target ${UPLOAD_GDB_REMOTE_KEYWORD} localhost:${GDB_PORT}
266+
${MBED_UPLOAD_LAUNCH_COMMANDS_FOR_GDBINIT}
267+
c"
268+
)
269+
270+
# Create target to start the GDB server
271+
add_custom_target(gdbserver
272+
COMMENT "Starting ${UPLOAD_METHOD} GDB server"
273+
COMMAND ${MBED_UPLOAD_GDBSERVER_DEBUG_COMMAND}
274+
USES_TERMINAL
275+
VERBATIM)
110276
endfunction(mbed_finalize_ide_debug_configurations)
111277

278+
else()
279+
280+
# No-ops
281+
function(mbed_generate_ide_debug_configuration CMAKE_TARGET)
282+
endfunction()
283+
284+
function(mbed_finalize_ide_debug_configurations)
285+
endfunction()
286+
112287
endif()

tools/cmake/mbed_target_functions.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ function(mbed_set_post_build target)
181181
mbed_generate_map_file(${target})
182182
endif()
183183

184-
mbed_generate_upload_debug_targets(${target})
184+
mbed_generate_upload_target(${target})
185185
mbed_generate_ide_debug_configuration(${target})
186186
endfunction()
187187

0 commit comments

Comments
 (0)