2
votes

is there any option can allow me to load key=value pairs from a dotenv file? I knew I can do -E env key=value But I have lots of values and I want to load them from the dotenv file, since other tools can load environment variables from it. I do not want to do bash tricks because I would like it to be as simple as possible and cross-platform.

1
A dotenv file is just a file with key=value pairs, one per line?Alex Reinking
@AlexReinking yesWang

1 Answers

3
votes

There's no built-in support for dotenv files, but here's a full implementation in pure CMake. For your CMakeLists.txt, here's an example:

cmake_minimum_required(VERSION 3.21)
project(env-test NONE)

add_custom_target(
  example
  COMMAND 
    ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/env.cmake 
    ${CMAKE_CURRENT_SOURCE_DIR}/.env  # dotenv
    env  # command
)

Here's the contents of the .env file:

foo=bar
bears=beets
battlestar=galactica

Here's a demo of this working:

$ cmake -G Ninja -S . -B build
$ cmake --build build --target example
...
foo=bar
bears=beets
battlestar=galactica

and here's the (complicated) env.cmake script that implements this:

cmake_minimum_required(VERSION 3.21)

if (CMAKE_ARGC LESS 5)
  message(FATAL_ERROR "Usage: cmake -P env.cmake <dotenv> [command...]")
endif ()

set(dotenv "${CMAKE_ARGV3}")

if (NOT EXISTS "${dotenv}")
  message(FATAL_ERROR "Dot-env file not found: ${dotenv}")
endif ()

set(command "")
math(EXPR argc "${CMAKE_ARGC} - 1")
foreach (i RANGE 4 ${argc})
  list(APPEND command "${CMAKE_ARGV${i}}")
endforeach ()

file(STRINGS "${dotenv}" entries)
foreach (entry IN LISTS entries)
  if (entry MATCHES "^([^=]+)=(.*)$")
    set(ENV{${CMAKE_MATCH_1}} "${CMAKE_MATCH_2}")
  else ()
    message(FATAL_ERROR "Malformed dotenv entry:\n${entry}")
  endif ()
endforeach ()

execute_process(COMMAND ${command} COMMAND_ERROR_IS_FATAL ANY)