diff --git a/.gitignore b/.gitignore index f24ad25c2..d723456a2 100644 --- a/.gitignore +++ b/.gitignore @@ -15,5 +15,8 @@ www/ .project .settings +# VSCode auto-generated files +.vscode + # CMake default directories build/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d56c7a8d..c4e813160 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ enable_testing() find_package(OmnetPP 5.5.1 MODULE REQUIRED) include(AddOppRun) include(AddOppTarget) +include(AddVSCode) include(CheckGitSubmodule) include(GenerateOppMessage) include(GNUInstallDirs) @@ -102,6 +103,8 @@ add_subdirectory(src/traci) add_subdirectory(src/artery) +option(VSCODE_LAUNCH_INTEGRATION "Generate VS Code configuration for debugging Artery (requires debug build)" OFF) + # scenarios directory is part of repository but omitted for Docker build context if(EXISTS ${PROJECT_SOURCE_DIR}/scenarios) add_subdirectory(scenarios) diff --git a/cmake/AddVSCode.cmake b/cmake/AddVSCode.cmake new file mode 100644 index 000000000..00f1e6122 --- /dev/null +++ b/cmake/AddVSCode.cmake @@ -0,0 +1,101 @@ +# Generates launch.json for VS Code, containing the default debug configuration. If PROJECT_SOURCE_DIR already +# contains a .vscode/launch.json, the default debug configuration is appended. +# +function(generate_vscode) + set(one_value_args "TARGET;FILE") + cmake_parse_arguments(args "${option_args}" "${one_value_args}" "" ${ARGN}) + + if(args_UNPARSED_ARGUMENTS) + message(SEND_ERROR "generate_vscode called with invalid arguments: ${args_UNPARSED_ARGUMENTS}") + endif() + + if(NOT args_TARGET) + message(SEND_ERROR "generate_vscode: TARGET argument is missing") + endif() + + if(NOT args_FILE) + message(SEND_ERROR "generate_vscode: FILE argument is missing") + endif() + + # collect all NED folders for given target + get_ned_folders(${args_TARGET} opp_run_ned_folders) + set(opp_run_ned_folders "\"-n$\"") + + # collect libraries for opp_run + set(opp_run_libraries "") + _get_opp_run_dependencies(${args_TARGET} opp_run_dependencies) + foreach(opp_run_dependency IN LISTS opp_run_dependencies) + list(APPEND opp_run_libraries "\"-l$\"") + endforeach() + set(opp_run_libraries "$") + + set(opp_run_executable ${OMNETPP_RUN_DEBUG}) + + # substitute variables first, then generator expressions, save in build directory + configure_file(${PROJECT_SOURCE_DIR}/cmake/launch.json.in ${PROJECT_BINARY_DIR}/launch.json.tmp @ONLY) + file(GENERATE OUTPUT ${PROJECT_BINARY_DIR}/launch.json.default INPUT ${PROJECT_BINARY_DIR}/launch.json.tmp) + + # if launch.json exists, find generated configuration and modify it + if(EXISTS ${args_FILE}) + set(default_config_found false) + file(READ ${args_FILE} current_launch_json) + + string(JSON num_configs LENGTH ${current_launch_json} "configurations") + message(STATUS "Found ${num_configs} VS Code Config(s) in .vscode/launch.json") + math(EXPR last_config "${num_configs} - 1") + + # read recent build configuration from temporary launch configuraiton + file(READ ${PROJECT_BINARY_DIR}/launch.json.default default_launch_json) + + # get generated default configuration + string(JSON default_launch_configuration GET ${default_launch_json} "configurations" 0) + + # get the default configuration name + string(JSON default_launch_configuration_name GET ${default_launch_json} "configurations" 0 "name") + + # get inputs required by default configuration + string(JSON default_launch_inputs GET ${default_launch_json} "inputs") + + # iterate available configs and search for the default config + foreach(config_id RANGE ${last_config}) + string(JSON config_name GET ${current_launch_json} "configurations" ${config_id} name) + + # found Arterys default debug configuration + if (${config_name} STREQUAL ${default_launch_configuration_name}) + message(STATUS "Updating Artery default config in .vscode/launch.json") + set(default_config_found true) + + # replace old config with newly generated one and save + string(JSON current_launch_json SET ${current_launch_json} "configurations" ${config_id} ${default_launch_configuration}) + file(WRITE ${args_FILE} ${current_launch_json}) + endif() + endforeach() + + # launch.json is present but no default configuration was found + if (NOT ${default_config_found}) + message(STATUS "Append Artery default config to .vscode/launch.json") + + # append default configuration + string(JSON current_launch_configuration SET ${current_launch_json} "configurations" ${num_configs} ${default_launch_configuration}) + + # append inputs to already existing inputs + string(JSON current_num_inputs LENGTH ${current_launch_configuration} "inputs") + string(JSON default_num_inputs LENGTH ${default_launch_inputs}) + math(EXPR last_default_input "${default_num_inputs} - 1") + + foreach(default_input_index RANGE ${last_default_input}) + string(JSON input GET ${default_launch_inputs} ${default_input_index}) + math(EXPR current_input_index "${current_num_inputs} + ${default_input_index}") + string(JSON current_launch_configuration SET ${current_launch_configuration} "inputs" ${current_input_index} ${input} ) + endforeach() + + file(WRITE ${args_FILE} ${current_launch_configuration}) + endif() + else() + message(STATUS "Generating default debug config for VS Code") + # substitute variables first, then generator expressions + file(GENERATE OUTPUT ${args_FILE} INPUT ${PROJECT_BINARY_DIR}/launch.json.default) + endif() + + +endfunction() diff --git a/cmake/launch.json.in b/cmake/launch.json.in new file mode 100644 index 000000000..db348f812 --- /dev/null +++ b/cmake/launch.json.in @@ -0,0 +1,77 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Artery in Debugger (generated)", + "description": "Generic debug configuration for Artey. It is (re-)generated with every CMake build generation and any modifications are overwritten!", + "type": "cppdbg", + "request": "launch", + + // Set path to OMNet++ debug runner + "program": "@opp_run_executable@", + + // Set working directory to scenario + "cwd": "${workspaceFolder}/scenarios/${input:scenario}", + + "args": [ + // Paths to NED files + @opp_run_ned_folders@, + + // Libraries used for Artery + @opp_run_libraries@, + + // .ini file to of the scenario + "omnetpp.ini", + + // OMNet++ config to run + "-c${input:config}", + // Run number + "-r${input:runId}", + + // OMNet++ interface can either be Qtenv or Cmdenv + // "-uQtenv", + "-uCmdenv" + ], + "stopAtEntry": false, + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Enable all-exceptions", + "text": "catch throw", + "ignoreFailures": true + } + ], + // "preLaunchTask": "C/C++: clang++ build active file", + "miDebuggerPath": "/usr/bin/gdb" + } + ], + "inputs": [ + { + "id": "scenario", + "type": "promptString", + "default": "artery", + "description": "Scenario folder to launch (provide the folder in ./scenarios/)" + }, + { + "id": "config", + "type": "promptString", + "default": "inet", + "description": "OMNeT++ config to launch" + }, + { + "id": "runId", + "type": "promptString", + "default": "0", + "description": "Run ID of the config to launch" + } + ] +} \ No newline at end of file diff --git a/src/artery/CMakeLists.txt b/src/artery/CMakeLists.txt index e221c38be..ff644a41e 100644 --- a/src/artery/CMakeLists.txt +++ b/src/artery/CMakeLists.txt @@ -183,3 +183,11 @@ if(NOT is_multi_config) PROGRAMS ${PROJECT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/run_artery.sh.install DESTINATION ${CMAKE_INSTALL_BINDIR} RENAME run_artery.sh) endif() + +if(VSCODE_LAUNCH_INTEGRATION) + if (NOT CMAKE_BUILD_TYPE OR "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + generate_vscode(TARGET artery FILE ${PROJECT_SOURCE_DIR}/.vscode/launch.json) + else() + message(STATUS "VS Code integration is enabled, but built type is Release") + endif() +endif()