diff --git a/CMakeLists.txt b/CMakeLists.txt index 5657473..8e5d847 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,22 +1,13 @@ -set(FIELDS2COVER_MAJOR_VERSION "2") -set(FIELDS2COVER_MINOR_VERSION "0") -set(FIELDS2COVER_PATCH_VERSION "0") +cmake_minimum_required(VERSION 3.12.4...3.22) -set(FIELDS2COVER_VERSION_STRING - "${FIELDS2COVER_MAJOR_VERSION}.${FIELDS2COVER_MINOR_VERSION}.${FIELDS2COVER_PATCH_VERSION}" -) -set(FIELDS2COVER_VERSION - ${FIELDS2COVER_MAJOR_VERSION}.${FIELDS2COVER_MINOR_VERSION}.${FIELDS2COVER_PATCH_VERSION} +project(Fields2Cover + VERSION 2.0.0 + DESCRIPTION "Fields2Cover is a complete coverage path planning package for autonomous robots" + HOMEPAGE_URL "http://fields2cover.github.io" + LANGUAGES CXX ) -set(PACKAGE_NAME "Fields2Cover") -set(PACKAGE_DESCRIPTION - "Fields2Cover is a complete coverage path planning package for autonomous robots" -) -set(PACKAGE_AUTHOR "Wageningen University") -set(PACKAGE_MAINTAINER "Gonzalo Mier") -set(PACKAGE_URL "http://fields2cover.github.io") option(ALLOW_PARALLELIZATION "Allow parallel algorithms" ON) @@ -29,17 +20,13 @@ option(USE_ORTOOLS_RELEASE "Get or-tools from release tarball" OFF) option(USE_ORTOOLS_FETCH_SRC "Get or-tools from source" OFF) option(USE_ORTOOLS_VENDOR "Get or-tools from ortools_vendor" OFF) -if (BUILD_PYTHON) - cmake_minimum_required(VERSION 3.12.4) -else(BUILD_PYTHON) - cmake_minimum_required(VERSION 3.11) -endif(BUILD_PYTHON) -if(${CMAKE_VERSION} VERSION_LESS 3.22) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -else(${CMAKE_VERSION} VERSION_LESS 3.22) - cmake_policy(VERSION 3.22) -endif(${CMAKE_VERSION} VERSION_LESS 3.22) +# Set C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + + if(POLICY CMP0094) cmake_policy(SET CMP0094 NEW) @@ -48,11 +35,6 @@ if(POLICY CMP0135) cmake_policy(SET CMP0135 NEW) endif(POLICY CMP0135) -project(Fields2Cover - LANGUAGES CXX - VERSION "${FIELDS2COVER_VERSION_STRING}" - DESCRIPTION "${PACKAGE_DESCRIPTION}" -) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) @@ -60,45 +42,11 @@ if(NOT CMAKE_BUILD_TYPE) # set(CMAKE_BUILD_TYPE Debug) endif(NOT CMAKE_BUILD_TYPE) -if (WIN32) - set(CMAKE_SHARED_LIBRARY_PREFIX "") -endif(WIN32) - -include(GNUInstallDirs) -list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules") +list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules") -include(FetchContent) -if(${CMAKE_VERSION} VERSION_LESS 3.14) - macro(FetchContent_MakeAvailable NAME) - FetchContent_GetProperties(${NAME}) - if(NOT ${NAME}_POPULATED) - FetchContent_Populate(${NAME}) - add_subdirectory(${${NAME}_SOURCE_DIR} ${${NAME}_BINARY_DIR}) - endif(NOT ${NAME}_POPULATED) - endmacro(FetchContent_MakeAvailable) -endif(${CMAKE_VERSION} VERSION_LESS 3.14) -set(CMAKE_POSITION_INDEPENDENT_CODE ON) -FetchContent_Declare(steering_functions FETCHCONTENT_UPDATES_DISCONNECTED - GIT_REPOSITORY https://github.com/Fields2Cover/steering_functions.git - GIT_TAG 13e3f5658144b3832fb1eb31a0e2f5a3cbf57db9 -) -FetchContent_Declare(matplot FETCHCONTENT_UPDATES_DISCONNECTED - GIT_REPOSITORY https://github.com/alandefreitas/matplotplusplus -) - -FetchContent_Declare(json FETCHCONTENT_UPDATES_DISCONNECTED - URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz -) - -FetchContent_MakeAvailable( - steering_functions - matplot - json -) - -set(THREADS_PREFER_PTHREAD_FLAG ON) +# Dependencies find_package(TinyXML2 REQUIRED) find_package(Threads REQUIRED) find_package(Gnuplot) @@ -106,150 +54,33 @@ find_package(GDAL 3.0 REQUIRED) find_package(Eigen3 REQUIRED) find_library(MATH_LIBRARY m) -set(ORTOOLS_TARGET "") - -if(USE_ORTOOLS_FETCH_SRC) - message(STATUS "or-tools -- Downloading and building from source") - set(BUILD_DEPS ON) - set(BUILD_SAMPLES OFF) - set(BUILD_EXAMPLES OFF) - - FetchContent_Declare(ortools FETCHCONTENT_UPDATES_DISCONNECTED - GIT_REPOSITORY https://github.com/google/or-tools.git - GIT_TAG v9.9 - CMAKE_ARGS - "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}" - -DBUILD_DEPS:BOOL=ON - -DBUILD_SAMPLES:BOOL=OFF - -DBUILD_EXAMPLES:BOOL=OFF - ) - FetchContent_MakeAvailable(ortools) - set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) - include(CTest) - set(ORTOOLS_TARGET "ortools") -elseif(USE_ORTOOLS_VENDOR) - find_package(ortools_vendor REQUIRED) -elseif(NOT USE_ORTOOLS_RELEASE) - find_package(ortools_vendor QUIET) - find_package(ortools CONFIG QUIET) -endif() - -if(USE_ORTOOLS_RELEASE OR (NOT ortools_FOUND)) - message(STATUS "or-tools -- Downloading and installing from release tarball") - if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") - message(STATUS "Target architecture is AMD64") - FetchContent_Declare(ortools FETCHCONTENT_UPDATES_DISCONNECTED - URL https://github.com/google/or-tools/releases/download/v9.9/or-tools_amd64_ubuntu-22.04_cpp_v9.9.3963.tar.gz - URL_HASH SHA256=a611133f4e9b75661c637347ebadff79539807cf8966eb9c176c2c560aad0a84 - ) - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") - message(STATUS "Target architecture is ARM64") - FetchContent_Declare(ortools FETCHCONTENT_UPDATES_DISCONNECTED - URL https://github.com/google/or-tools/releases/download/v9.9/or-tools_arm64_debian-11_cpp_v9.9.3963.tar.gz - URL_HASH SHA256=f308a06d89dce060f74e6fec4936b43f4bdf4874d18c131798697756200f4e7a - ) - else() - message(FATAL_ERROR "Unknown/Unhandled target architecture: ${CMAKE_SYSTEM_PROCESSOR}") - endif() - #NOTE: FetchContent_GetProperties variables only available in called scope - FetchContent_GetProperties(ortools) - if(NOT ortools_POPULATED) - FetchContent_Populate(ortools) - endif() - list(INSERT CMAKE_PREFIX_PATH 0 "${ortools_SOURCE_DIR}") - find_package(ortools CONFIG REQUIRED) - if(NOT ortools_FOUND) - message(FATAL_ERROR "Failed to find ortools in release tarball") - endif() - - #NOTE: in CMAKE 3.21 introduces IMPORTED_RUNTIME_ARTIFACTS - # https://cmake.org/cmake/help/v3.21/command/install.html#imported-runtime-artifacts - # Which would allow creation of an ortools target with IMPORTED_LOCATION - # and then install with IMPORTED_RUNTIME_ARTIFACTS - # Just brute force install the contents of the directories for now. - install( - DIRECTORY "${ortools_SOURCE_DIR}/" - DESTINATION opt/f2c_ortools/ - ) -endif(USE_ORTOOLS_RELEASE OR (NOT ortools_FOUND)) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(THREADS_PREFER_PTHREAD_FLAG ON) + + +include(cmake/F2CUtils.cmake) +f2c_declare_dependencies() -set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) file(GLOB_RECURSE fields2cover_src "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/src/*/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/src/*/*/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/src/*/*/*/*.cpp" -) - -list(REMOVE_ITEM fields2cover_src - "${CMAKE_CURRENT_SOURCE_DIR}/.*" - "${CMAKE_CURRENT_SOURCE_DIR}/src/.*" - "${CMAKE_CURRENT_SOURCE_DIR}/src/*/.*" - "${CMAKE_CURRENT_SOURCE_DIR}/src/*/*/.*" - "${CMAKE_CURRENT_SOURCE_DIR}/src/*/*/*/.*.cpp" ) -if(BUILD_SHARED_LIBS) - add_library(Fields2Cover SHARED ${fields2cover_src}) -else(BUILD_SHARED_LIBS) - add_library(Fields2Cover STATIC ${fields2cover_src}) -endif(BUILD_SHARED_LIBS) +add_library(Fields2Cover ${fields2cover_src}) -target_compile_features(Fields2Cover PUBLIC cxx_std_17) - -if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") - target_compile_options(Fields2Cover PRIVATE - -Wall -Wfatal-errors - -Wno-dev -Wextra -Wno-unused-parameter -Wno-sign-compare - -Wno-attributes -Wl,--no-undefined - ) -elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") - target_compile_options(Fields2Cover PRIVATE /W4) -elseif(${CMAKE_CXX_COMPILER_ID} MATCHES "^(Apple)?Clang$") - -endif(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") - - - -if (CMAKE_BUILD_TYPE STREQUAL "Debug") - include(cmake/Modules/CodeCoverage.cmake) - APPEND_COVERAGE_COMPILER_FLAGS() - #if(NOT USE_ORTOOLS_FETCH_SRC) - # target_compile_options(Fields2Cover PRIVATE -pedantic -Werror) - #endif() - target_compile_options(Fields2Cover PRIVATE - -g --coverage -fprofile-arcs -ftest-coverage -fno-inline - ) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov") - - if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - target_compile_options(Fields2Cover PRIVATE /O0) - else(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - target_compile_options(Fields2Cover PRIVATE -O0) - endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - -else (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") - if (${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") - target_compile_options(Fields2Cover PRIVATE /O2) - else(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") - target_compile_options(Fields2Cover PRIVATE -O2) - endif(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") -endif(CMAKE_BUILD_TYPE STREQUAL "Debug") - - -target_include_directories(Fields2Cover PUBLIC - $ - $ - $ - ${EIGEN3_INCLUDE_DIRS} +target_include_directories(Fields2Cover + PUBLIC + $ + $ + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src ) target_link_libraries(Fields2Cover PUBLIC - ${GDAL_LIBRARIES} + GDAL::GDAL ${CMAKE_THREAD_LIBS_INIT} -lgeos_c ${MATH_LIBRARY} @@ -261,80 +92,57 @@ target_link_libraries(Fields2Cover matplot ) - if(ALLOW_PARALLELIZATION) target_link_libraries(Fields2Cover PRIVATE tbb) endif(ALLOW_PARALLELIZATION) -set_target_properties(Fields2Cover - PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE - PUBLIC_HEADER "include/fields2cover.h" -) -install( - TARGETS - Fields2Cover - steering_functions - matplot - ${ORTOOLS_TARGET} - EXPORT Fields2Cover-targets - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" - PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" -) -#https://cmake.org/cmake/help/latest/guide/tutorial/Adding%20Export%20Configuration.html -include(CMakePackageConfigHelpers) -write_basic_package_version_file(Fields2CoverConfigVersion.cmake - VERSION ${PACKAGE_VERSION} - COMPATIBILITY AnyNewerVersion + +# Compiler options +include(cmake/F2CCompilerOptions.cmake) +f2c_set_compiler_options(Fields2Cover) + + +include(GNUInstallDirs) +set(F2C_INSTALL_CMAKEDIR ${CMAKE_INSTALL_LIBDIR}/cmake/Fields2Cover) + +install(TARGETS Fields2Cover steering_functions matplot + EXPORT Fields2CoverTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) -install( - EXPORT Fields2Cover-targets - FILE Fields2Cover-Targets.cmake - DESTINATION "lib/cmake/fields2cover" +install(DIRECTORY include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) -# generate the config file that includes the exports +include(CMakePackageConfigHelpers) configure_package_config_file( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Fields2CoverConfig.cmake.in - "${CMAKE_CURRENT_BINARY_DIR}/Fields2CoverConfig.cmake" - INSTALL_DESTINATION "lib/cmake/fields2cover" - NO_SET_AND_CHECK_MACRO - NO_CHECK_REQUIRED_COMPONENTS_MACRO + ${CMAKE_CURRENT_BINARY_DIR}/Fields2CoverConfig.cmake + INSTALL_DESTINATION ${F2C_INSTALL_CMAKEDIR} + PATH_VARS CMAKE_INSTALL_INCLUDEDIR CMAKE_INSTALL_LIBDIR ) + write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/Fields2CoverConfigVersion.cmake" - VERSION "${Tutorial_VERSION_MAJOR}.${Tutorial_VERSION_MINOR}" - COMPATIBILITY AnyNewerVersion + ${CMAKE_CURRENT_BINARY_DIR}/Fields2CoverConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion ) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/Fields2CoverConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/Fields2CoverConfigVersion.cmake - DESTINATION "lib/cmake/fields2cover" -) - -install( - FILES ${CMAKE_CURRENT_SOURCE_DIR}/package.xml - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/fields2cover -) - -export( - TARGETS - Fields2Cover - steering_functions - matplot - ${ORTOOLS_TARGET} - FILE - Fields2Cover-${type}-Targets.cmake + DESTINATION ${F2C_INSTALL_CMAKEDIR} ) -# Copy public headers during `make install` -install( - DIRECTORY include/ - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/fields2cover +install(EXPORT Fields2CoverTargets + FILE Fields2CoverTargets.cmake + NAMESPACE Fields2Cover:: + DESTINATION ${F2C_INSTALL_CMAKEDIR} ) diff --git a/cmake/F2CCompilerOptions.cmake b/cmake/F2CCompilerOptions.cmake new file mode 100644 index 0000000..6eb7491 --- /dev/null +++ b/cmake/F2CCompilerOptions.cmake @@ -0,0 +1,30 @@ + +function(f2c_set_compiler_options target) + if(MSVC) + target_compile_options(${target} PRIVATE /W4 /WX) + else() + target_compile_options(${target} PRIVATE + -Wall -Wextra -Werror -pedantic + -Wno-unused-parameter -Wno-sign-compare + ) + endif() + + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + include(cmake/Modules/CodeCoverage.cmake) + APPEND_COVERAGE_COMPILER_FLAGS() + if(MSVC) + target_compile_options(${target} PRIVATE /Od /Zi) + else() + target_compile_options(${target} PRIVATE -g --coverage -fprofile-arcs -ftest-coverage -fno-inline -O0) + target_link_options(${target} PRIVATE --coverage) + target_link_libraries(${target} PRIVATE gcov) + + endif() + elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + if(MSVC) + target_compile_options(${target} PRIVATE /O2) + else() + target_compile_options(${target} PRIVATE -O2) + endif() + endif() +endfunction() diff --git a/cmake/F2CUtils.cmake b/cmake/F2CUtils.cmake new file mode 100644 index 0000000..7162633 --- /dev/null +++ b/cmake/F2CUtils.cmake @@ -0,0 +1,83 @@ +function(f2c_declare_dependencies) + + include(FetchContent) + if(${CMAKE_VERSION} VERSION_LESS 3.14) + macro(FetchContent_MakeAvailable NAME) + FetchContent_GetProperties(${NAME}) + if(NOT ${NAME}_POPULATED) + FetchContent_Populate(${NAME}) + add_subdirectory(${${NAME}_SOURCE_DIR} ${${NAME}_BINARY_DIR}) + endif(NOT ${NAME}_POPULATED) + endmacro(FetchContent_MakeAvailable) + endif(${CMAKE_VERSION} VERSION_LESS 3.14) + + + set(ORTOOLS_TARGET "") + if(USE_ORTOOLS_FETCH_SRC) + message(STATUS "or-tools -- Downloading and building from source") + FetchContent_Declare(ortools + GIT_REPOSITORY https://github.com/google/or-tools.git + GIT_TAG v9.9 + ) + FetchContent_MakeAvailable(ortools) + set(ORTOOLS_TARGET "ortools") + elseif(USE_ORTOOLS_VENDOR) + find_package(ortools_vendor REQUIRED) + else() + find_package(ortools_vendor QUIET) + find_package(ortools CONFIG QUIET) + if(NOT ortools_FOUND) + message(STATUS "or-tools -- Downloading and installing from release tarball") + if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + message(STATUS "Target architecture is AMD64") + FetchContent_Declare(ortools FETCHCONTENT_UPDATES_DISCONNECTED + URL https://github.com/google/or-tools/releases/download/v9.9/or-tools_amd64_ubuntu-22.04_cpp_v9.9.3963.tar.gz + URL_HASH SHA256=a611133f4e9b75661c637347ebadff79539807cf8966eb9c176c2c560aad0a84 + ) + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") + message(STATUS "Target architecture is ARM64") + FetchContent_Declare(ortools FETCHCONTENT_UPDATES_DISCONNECTED + URL https://github.com/google/or-tools/releases/download/v9.9/or-tools_arm64_debian-11_cpp_v9.9.3963.tar.gz + URL_HASH SHA256=f308a06d89dce060f74e6fec4936b43f4bdf4874d18c131798697756200f4e7a + ) + else() + message(FATAL_ERROR "Unknown/Unhandled target architecture: ${CMAKE_SYSTEM_PROCESSOR}") + endif() + #NOTE: FetchContent_GetProperties variables only available in called scope + FetchContent_GetProperties(ortools) + if(NOT ortools_POPULATED) + FetchContent_Populate(ortools) + endif() + list(INSERT CMAKE_PREFIX_PATH 0 "${ortools_SOURCE_DIR}") + find_package(ortools CONFIG REQUIRED) + if(NOT ortools_FOUND) + message(FATAL_ERROR "Failed to find ortools in release tarball") + endif() + + #NOTE: in CMAKE 3.21 introduces IMPORTED_RUNTIME_ARTIFACTS + # https://cmake.org/cmake/help/v3.21/command/install.html#imported-runtime-artifacts + # Which would allow creation of an ortools target with IMPORTED_LOCATION + # and then install with IMPORTED_RUNTIME_ARTIFACTS + # Just brute force install the contents of the directories for now. + include(GNUInstallDirs) + install( + DIRECTORY "${ortools_SOURCE_DIR}/" + DESTINATION ${CMAKE_INSTALL_PREFIX} #opt/f2c_ortools/ + ) + endif(NOT ortools_FOUND) + endif() + + FetchContent_Declare(steering_functions FETCHCONTENT_UPDATES_DISCONNECTED + GIT_REPOSITORY https://github.com/Fields2Cover/steering_functions.git + GIT_TAG 13e3f5658144b3832fb1eb31a0e2f5a3cbf57db9 + ) + FetchContent_Declare(matplot FETCHCONTENT_UPDATES_DISCONNECTED + GIT_REPOSITORY https://github.com/alandefreitas/matplotplusplus + ) + + FetchContent_Declare(json FETCHCONTENT_UPDATES_DISCONNECTED + URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz + ) + + FetchContent_MakeAvailable(steering_functions matplot json) +endfunction() diff --git a/cmake/Fields2CoverConfig.cmake.in b/cmake/Fields2CoverConfig.cmake.in index bec53f9..92a61de 100644 --- a/cmake/Fields2CoverConfig.cmake.in +++ b/cmake/Fields2CoverConfig.cmake.in @@ -1,12 +1,21 @@ @PACKAGE_INIT@ -include("${CMAKE_CURRENT_LIST_DIR}/Fields2Cover-Targets.cmake" ) +include("${CMAKE_CURRENT_LIST_DIR}/Fields2CoverTargets.cmake") include(CMakeFindDependencyMacro) + +# Find dependencies find_dependency(GDAL 3.0 REQUIRED) +find_dependency(Threads REQUIRED) +find_dependency(Eigen3 REQUIRED) + +# Optional dependencies +if(@F2C_ALLOW_PARALLELIZATION@) + find_dependency(TBB REQUIRED) +endif() + +set_and_check(Fields2Cover_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") +check_required_components(Fields2Cover) -list(INSERT CMAKE_PREFIX_PATH 0 "${PACKAGE_PREFIX_DIR}/opt/ortools_vendor") -list(INSERT CMAKE_PREFIX_PATH 1 "${PACKAGE_PREFIX_DIR}/opt/f2c_ortools") -find_dependency(ortools REQUIRED NO_SYSTEM_ENVIRONMENT_PATH)