diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c7a502..1bebe33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,8 @@ #----------------------- PROJECT CONFIGURATION -------------------------------- -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.16) project(cpp-dotenv VERSION 1.0.0) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) if ("${CMAKE_BUILD_TYPE}" STREQUAL "") @@ -12,6 +12,21 @@ else() endif() message(STATUS "Building CPP-DOTENV in ${CMAKE_BUILD_TYPE} mode") +# Option to build both static and shared libraries +option(BUILD_BOTH_LIBRARIES "Build both static and shared libraries" OFF) + +# Validate that BUILD_BOTH_LIBRARIES and BUILD_SHARED_LIBS are not used together +if(BUILD_BOTH_LIBRARIES AND BUILD_SHARED_LIBS) + message(WARNING "BUILD_BOTH_LIBRARIES and BUILD_SHARED_LIBS should not be used together. BUILD_SHARED_LIBS will be ignored.") + set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries" FORCE) +endif() + +# When building shared libraries that link against static libraries, +# we need position-independent code for the static libraries +if(BUILD_SHARED_LIBS OR BUILD_BOTH_LIBRARIES) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) +endif() + #------------------- SUBDIRECTORY ADDITION ------------------------------------ add_subdirectory(common) @@ -19,29 +34,82 @@ add_subdirectory(src) #----------------------- LIBRARY CONFIGURATION -------------------------------- -set(CPP_DOTENV cpp_dotenv CACHE INTERNAL "") set(CPP_DOTENV_SRC src/dotenv.cpp include/dotenv.h ) -add_library(${CPP_DOTENV} ${CPP_DOTENV_SRC}) +# Common include directories +set(CPP_DOTENV_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(${CPP_DOTENV} - ${ENVIRON_LIB} - ${PARSER_LIB} -) +# Common compile options - handle MSVC vs GCC/Clang +if (MSVC) + # MSVC-specific flags + if ("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG") + set(CPP_DOTENV_COMPILE_OPTIONS /W4 /Od) + else() + set(CPP_DOTENV_COMPILE_OPTIONS /W4 /O2) + endif() +else() + # GCC/Clang and other compilers using Unix-style flags + if ("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG") + set(CPP_DOTENV_COMPILE_OPTIONS -g -Wall -O0) + else() + set(CPP_DOTENV_COMPILE_OPTIONS -O3) + endif() +endif() -target_include_directories(${CPP_DOTENV} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include -) +# Build both static and shared libraries if requested +if(BUILD_BOTH_LIBRARIES) + # Static library + add_library(cpp_dotenv_static STATIC ${CPP_DOTENV_SRC}) + target_link_libraries(cpp_dotenv_static + ${ENVIRON_LIB} + ${PARSER_LIB} + ) + target_include_directories(cpp_dotenv_static PUBLIC ${CPP_DOTENV_INCLUDE_DIRS}) + target_compile_options(cpp_dotenv_static PRIVATE ${CPP_DOTENV_COMPILE_OPTIONS}) + # Set output name to libcpp_dotenv.a (instead of libcpp_dotenv_static.a) + set_target_properties(cpp_dotenv_static PROPERTIES OUTPUT_NAME cpp_dotenv) -if ("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG") - target_compile_options(${CPP_DOTENV} PRIVATE - -g -Wall -O0 + # Shared library + add_library(cpp_dotenv_shared SHARED ${CPP_DOTENV_SRC}) + target_link_libraries(cpp_dotenv_shared + ${ENVIRON_LIB} + ${PARSER_LIB} ) + target_include_directories(cpp_dotenv_shared PUBLIC ${CPP_DOTENV_INCLUDE_DIRS}) + target_compile_options(cpp_dotenv_shared PRIVATE ${CPP_DOTENV_COMPILE_OPTIONS}) + # Set output name. On non-MSVC platforms, both static and shared libraries use + # the same base name "cpp_dotenv". On MSVC, give the shared library a distinct + # base name to avoid conflicts between the static library (.lib) and the + # import library for the shared library (also .lib). + if(MSVC) + set_target_properties(cpp_dotenv_shared PROPERTIES OUTPUT_NAME cpp_dotenv_shared) + else() + set_target_properties(cpp_dotenv_shared PROPERTIES OUTPUT_NAME cpp_dotenv) + endif() + + # Alias for backward compatibility: + # When BUILD_BOTH_LIBRARIES is ON, the cpp_dotenv target is an alias to + # cpp_dotenv_static. Projects that need the shared library must explicitly + # link against cpp_dotenv_shared instead of cpp_dotenv. + add_library(cpp_dotenv ALIAS cpp_dotenv_static) else() + # Single library (type determined by BUILD_SHARED_LIBS) + set(CPP_DOTENV cpp_dotenv CACHE INTERNAL "") + add_library(${CPP_DOTENV} ${CPP_DOTENV_SRC}) + + target_link_libraries(${CPP_DOTENV} + ${ENVIRON_LIB} + ${PARSER_LIB} + ) + + target_include_directories(${CPP_DOTENV} PUBLIC + ${CPP_DOTENV_INCLUDE_DIRS} + ) + target_compile_options(${CPP_DOTENV} PRIVATE - -O3 + ${CPP_DOTENV_COMPILE_OPTIONS} ) endif() diff --git a/README.md b/README.md index aad33c5..24a0a63 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,11 @@ C++ implementation of NodeJS [dotenv](https://github.com/motdotla/dotenv) projec Supported build methods are: -- [CMake](#cmake) (>=3.10) +- [CMake](#cmake) (>=3.16) ### CMake -**cpp-dotenv** comes with support for `CMake` right out of the box. In order to use it, simply include this repository's directory and link the `cpp_dotenv` target to your own targets where needed: +**cpp-dotenv** comes with support for `CMake` right out of the box (>=3.16). In order to use it, simply include this repository's directory and link the `cpp_dotenv` target to your own targets where needed: ```cmake add_subdirectory(cpp-dotenv) @@ -55,6 +55,42 @@ target_link_libraries(YOUR_TARGET cpp_dotenv) After this, you might use the library as described in [usage](#usage); no extra scoping, no need to worry about the project's directory structure. +#### Build Options + +By default, only one library target named `cpp_dotenv` is built. The type of library (static or shared) is controlled by CMake's standard `BUILD_SHARED_LIBS` option: + +```bash +# Build static library (default) +cmake -DBUILD_SHARED_LIBS=OFF .. + +# Build shared library +cmake -DBUILD_SHARED_LIBS=ON .. +``` + +#### Building Both Static and Shared Libraries + +To build both static and shared libraries in a single build directory, use the `BUILD_BOTH_LIBRARIES` option: + +```bash +cmake -DBUILD_BOTH_LIBRARIES=ON .. +``` + +This creates two separate library targets: +- `cpp_dotenv_static` - outputs as `libcpp_dotenv.a` +- `cpp_dotenv_shared` - outputs as `libcpp_dotenv.so` (or `.dylib` on macOS) + +For backward compatibility, the `cpp_dotenv` target remains available as an alias to `cpp_dotenv_static`. If you need to link against the shared library, explicitly specify `cpp_dotenv_shared`: + +```cmake +# Link against static library (default) +target_link_libraries(YOUR_TARGET cpp_dotenv) + +# Link against shared library +target_link_libraries(YOUR_TARGET cpp_dotenv_shared) +``` + +**Note:** When `BUILD_BOTH_LIBRARIES` is enabled, `BUILD_SHARED_LIBS` is ignored. Do not use both options simultaneously. + ## Usage To be able to use the dotenv classes, simply include the main header file: diff --git a/common/libs/antlr4-cpp-runtime/CMakeLists.txt b/common/libs/antlr4-cpp-runtime/CMakeLists.txt index 1b488f2..e00a12c 100644 --- a/common/libs/antlr4-cpp-runtime/CMakeLists.txt +++ b/common/libs/antlr4-cpp-runtime/CMakeLists.txt @@ -1,7 +1,7 @@ -cmake_minimum_required(VERSION 3.0.2) +cmake_minimum_required(VERSION 3.16) project(antlr4-cpp-runtime VERSION 4.8) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) if ("${CMAKE_BUILD_TYPE}" STREQUAL "") diff --git a/include/dotenv.h b/include/dotenv.h index 1b3a9c8..3d786d6 100644 --- a/include/dotenv.h +++ b/include/dotenv.h @@ -15,7 +15,7 @@ namespace dotenv public: - dotenv& load_dotenv(const std::string& dotenv_path = env_filename, + dotenv& load_dotenv(const std::string& dotenv_path = ".env", const bool overwrite = false, const bool interpolate = true); @@ -28,18 +28,27 @@ namespace dotenv void operator=(const dotenv&) = delete; static dotenv& instance(); - + private: dotenv() = default; private: + // Declare static members (definitions are inline after the class) + // NOTE: env_filename is kept for backward compatibility; external code + // may reference dotenv::dotenv::env_filename directly. static const std::string env_filename; static dotenv _instance; }; + // C++17 inline variable definitions after class is complete + // Note: _instance cannot be inline inside the class due to incomplete type + inline const std::string dotenv::env_filename = ".env"; + inline dotenv dotenv::_instance; + + extern dotenv& env; } diff --git a/src/common/logger.cpp b/src/common/logger.cpp index 9430cf1..b335605 100644 --- a/src/common/logger.cpp +++ b/src/common/logger.cpp @@ -21,7 +21,7 @@ bool logger::position::operator<(const position& p) const } -bool logger::position::less::operator()(const position& p1, const position& p2) +bool logger::position::less::operator()(const position& p1, const position& p2) const { return p1 < p2; } diff --git a/src/common/logger.h b/src/common/logger.h index f2a91a3..4daa68e 100644 --- a/src/common/logger.h +++ b/src/common/logger.h @@ -31,7 +31,7 @@ namespace dotenv struct less { - bool operator()(const position& p1, const position& p2); + bool operator()(const position& p1, const position& p2) const; }; public: diff --git a/src/dotenv.cpp b/src/dotenv.cpp index 89bd3c1..9e55b18 100644 --- a/src/dotenv.cpp +++ b/src/dotenv.cpp @@ -8,10 +8,14 @@ using namespace std; -using namespace dotenv; -dotenv::dotenv& dotenv::dotenv::load_dotenv(const string& dotenv_path, const bool overwrite, const bool interpolate) +// Type alias to work around the fact that class name matches namespace name +// This is a known C++ limitation when defining out-of-line members +typedef class ::dotenv::dotenv DotenvClass; + + +DotenvClass& ::dotenv::dotenv::load_dotenv(const string& dotenv_path, const bool overwrite, const bool interpolate) { ifstream env_file; env_file.open(dotenv_path); @@ -27,19 +31,17 @@ dotenv::dotenv& dotenv::dotenv::load_dotenv(const string& dotenv_path, const boo } -const dotenv::dotenv::value_type dotenv::dotenv::operator[](const key_type& k) const +const string DotenvClass::operator[](const key_type& k) const { return getenv(k).second; } -dotenv::dotenv& dotenv::dotenv::instance() +DotenvClass& ::dotenv::dotenv::instance() { return _instance; } -const string dotenv::dotenv::env_filename = ".env"; -dotenv::dotenv dotenv::dotenv::_instance; - -dotenv::dotenv& dotenv::env = dotenv::instance(); +// Define the global 'env' reference. Static members are inline in the header (C++17). +::dotenv::dotenv& ::dotenv::env = ::dotenv::dotenv::instance();