diff --git a/VERSION b/VERSION index 8294c18..b1e80bb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.2 \ No newline at end of file +0.1.3 diff --git a/cmake/libe3.pc.in b/cmake/libe3.pc.in index cd36b94..b37b934 100644 --- a/cmake/libe3.pc.in +++ b/cmake/libe3.pc.in @@ -1,8 +1,8 @@ -prefix=@CMAKE_INSTALL_PREFIX@ +prefix=@LIBE3_PC_PREFIX@ exec_prefix=${prefix} -libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ -includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ -datarootdir=${prefix}/@CMAKE_INSTALL_DATADIR@ +libdir=@LIBE3_PC_LIBDIR@ +includedir=@LIBE3_PC_INCLUDEDIR@ +datarootdir=@LIBE3_PC_DATAROOTDIR@ # Root of the installed service-model definitions; each SM lives in its own # subdir, e.g. ${smdir}/sm_simple/e3sm_simple.asn (ASN.1) and .../e3sm_simple.proto # (Protobuf) — both are installed. Query with: pkg-config --variable=smdir libe3 diff --git a/cmake/libe3Install.cmake b/cmake/libe3Install.cmake index 4982b1f..9b033df 100644 --- a/cmake/libe3Install.cmake +++ b/cmake/libe3Install.cmake @@ -24,10 +24,66 @@ if(LIBE3_PUBLIC_DEFS) "libe3: PUBLIC compile definition '${_def}' is a generator " "expression and cannot be exported through libe3.pc") endif() + if(_def MATCHES "\"") + # pkg-config's Cflags tokenizer strips unescaped quotes, so a string + # macro exported this way reaches the compiler as a bare token and + # fails to lex. Escaping as \\" survives pkg-config and works for + # CMake consumers, but then breaks `cc $(pkg-config --cflags libe3)` + # with an unterminated string -- no single form is safe for both + # consumption styles. Keep string-valued macros PRIVATE and give the + # header an #ifndef fallback instead (see LATREC_DEFAULT_DIR). + message(FATAL_ERROR + "libe3: PUBLIC compile definition '${_def}' has a quoted value " + "and cannot be exported through libe3.pc -- make it PRIVATE") + endif() string(APPEND LIBE3_PC_CFLAGS " -D${_def}") endforeach() endif() +# Make libe3.pc relocatable. CMAKE_INSTALL_PREFIX is a configure-time value, so +# baking it in makes the file describe where the build *expected* to be installed +# rather than where it ended up: `cmake --install --prefix `, and any +# packaging step that stages into a different root, moves every file but cannot +# rewrite the .pc. The result points a consumer's -I/-L and `smdir` at a prefix +# that may contain no libe3 at all -- and pkg-config reports success while doing +# it, so the failure surfaces later as a missing header. pkg-config expands +# ${pcfiledir} to the directory holding the .pc, so deriving the prefix from that +# describes wherever the file actually is. The CMake package config is already +# relocatable through @PACKAGE_INIT@; this makes the pkg-config path agree. +# +# GNUInstallDirs allows any component directory to be absolute, in which case it +# is not under the prefix and there is nothing relative to derive -- those keep +# their absolute value, and an absolute libdir also leaves the prefix itself with +# no relative form, since that is where the .pc lands. +if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") + set(LIBE3_PC_PREFIX "${CMAKE_INSTALL_PREFIX}") +else() + file(RELATIVE_PATH _libe3_pc_to_prefix + "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/pkgconfig" + "${CMAKE_INSTALL_PREFIX}") + string(REGEX REPLACE "/+$" "" _libe3_pc_to_prefix "${_libe3_pc_to_prefix}") + if(_libe3_pc_to_prefix STREQUAL "") + set(LIBE3_PC_PREFIX "\${pcfiledir}") + else() + set(LIBE3_PC_PREFIX "\${pcfiledir}/${_libe3_pc_to_prefix}") + endif() +endif() + +# CMAKE_INSTALL_DATADIR is empty in the cache until GNUInstallDirs derives it +# from DATAROOTDIR, so read the derived value rather than the cache entry. +foreach(_pair "LIBDIR:${CMAKE_INSTALL_LIBDIR}" + "INCLUDEDIR:${CMAKE_INSTALL_INCLUDEDIR}" + "DATAROOTDIR:${CMAKE_INSTALL_DATAROOTDIR}") + string(REPLACE ":" ";" _pair "${_pair}") + list(GET _pair 0 _name) + list(GET _pair 1 _dir) + if(IS_ABSOLUTE "${_dir}") + set(LIBE3_PC_${_name} "${_dir}") + else() + set(LIBE3_PC_${_name} "\${prefix}/${_dir}") + endif() +endforeach() + configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/libe3.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/libe3.pc" diff --git a/cmake/libe3Targets.cmake b/cmake/libe3Targets.cmake index 65df668..fc76750 100644 --- a/cmake/libe3Targets.cmake +++ b/cmake/libe3Targets.cmake @@ -82,8 +82,17 @@ if(LIBE3_ENABLE_LATREC) # registry compiled in, or vice versa. target_compile_definitions(libe3 PUBLIC LIBE3_ENABLE_LATREC) if(LATREC_DEFAULT_DIR) + # PRIVATE, unlike the flag above. latrec_open_in() applies this fallback + # from src/core/latrec.c -- libe3's own translation unit -- so every + # caller of latrec_tls_open_as() inherits it without needing the macro + # itself. Exporting it would put a quoted string macro in libe3.pc, + # where pkg-config's tokenizer strips the quotes and the bare path then + # fails to lex ("expected expression before '/'"). Escaping as \\" keeps + # CMake consumers working but breaks `cc $(pkg-config --cflags libe3)` + # with an unterminated string, so there is no form that is safe for + # both. It is also a build-tree path, meaningless once installed. target_compile_definitions(libe3 - PUBLIC LATREC_DEFAULT_DIR=\"${LATREC_DEFAULT_DIR}\") + PRIVATE LATREC_DEFAULT_DIR=\"${LATREC_DEFAULT_DIR}\") endif() endif() @@ -152,8 +161,9 @@ endif() if(LIBE3_ENABLE_LATREC) target_compile_definitions(libe3_shared PUBLIC LIBE3_ENABLE_LATREC) if(LATREC_DEFAULT_DIR) + # PRIVATE for the same reason as the static target above. target_compile_definitions(libe3_shared - PUBLIC LATREC_DEFAULT_DIR=\"${LATREC_DEFAULT_DIR}\") + PRIVATE LATREC_DEFAULT_DIR=\"${LATREC_DEFAULT_DIR}\") endif() endif()