Repository: conan-io/conan-center-index

Issue:
[package] jwt-cpp/0.6.0: Package doesn't compile because of missing picojson requirement
### Package and Environment Details

* Package Name/Version: **jwt-cpp/0.6.0**
* Package Revision: **9ba33a57e1b44045f50633246c660de4**
* Operating System+version: **Arch linux**
* Compiler+version: **GCC 11**
* Conan version: **conan 1.51.3**


### Conan profile

[settings]
arch=x86_64
build_type=Debug
compiler=gcc
compiler.libcxx=libstdc++11
compiler.version=12
os=Linux
os_build=Linux
arch_build=x86_64
[options]
[conf]
[build_requires]
[env]

### Steps to reproduce

1. Add jwt-cpp/0.6.0 dependency
2. Try to conan install with build=missing
3. Get an error: `.../jwt.h:8:10: fatal error: picojson.h: No such file or directory`

### Logs

<details><summary>Click to expand log</summary>

```
/home/popov/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:8:10: fatal error: picojson.h: No such file or directory
    8 | #include "picojson.h"
      |          ^~~~~~~~~~~~
```

</details>



Discussion:
There is a check in the receipt:
```
if not self._supports_generic_json:
            self.requires("picojson/1.3.0")
```
In my case `_supports_generic_json` should return true so that's the reason why picojson isn't required. But in that case it seems like `JWT_DISABLE_PICOJSON` definition should be provided.
Correct, can you submit a PR? Just need to add `self.cpp_info.defines.append("JWT_DISABLE_PICOJSON")` in `package_info()` if `self._supports_generic_json`.

Also need to remove `picojson` in requirements of test_package, because it defeats the purpose of test_package. /cc @prince-chrismc (https://github.com/conan-io/conan-center-index/pull/4644)
firstly thanks for trying jwt-cpp (I am a contributor)!🤔 there's definitely something missing in the recipe

Adding the define it definitely a first step.

As for Space's suggestion, just two notes...

The test_package would still need a JSON library to compile, and you would need to `#undef JWT_DISABLE_PICOJSON` as well...
You are expected to add a JSON library so the disable is fine but you need to add the `traits/.../defaults.h` include for it to work in that case.





Resolving patch:
diff --git a/recipes/jwt-cpp/all/conanfile.py b/recipes/jwt-cpp/all/conanfile.py
--- a/recipes/jwt-cpp/all/conanfile.py
+++ b/recipes/jwt-cpp/all/conanfile.py
@@ -1,4 +1,7 @@
-from conans import ConanFile, tools
+from conan import ConanFile
+from conan.tools.scm import Version
+from conan.tools.files import get, copy, apply_conandata_patches
+from conan.tools.layout import basic_layout
 import os
 
 required_conan_version = ">=1.43.0"
@@ -10,15 +13,12 @@ class JwtCppConan(ConanFile):
     homepage = "https://github.com/Thalhammer/jwt-cpp"
     description = "A C++ JSON Web Token library for encoding/decoding"
     topics = ("jwt-cpp", "json", "jwt", "jws", "jwe", "jwk", "jwks", "jose", "header-only")
+    settings = "arch", "build_type", "compiler", "os"
     no_copy_source = True
 
-    @property
-    def _source_subfolder(self):
-        return "source_subfolder"
-
     @property
     def _supports_generic_json(self):
-        return tools.Version(self.version) >= "0.5.0"
+        return Version(self.version) >= "0.5.0"
 
     def export_sources(self):
         for patch in self.conan_data.get("patches", {}).get(self.version, []):
@@ -30,18 +30,22 @@ def requirements(self):
             self.requires("picojson/1.3.0")
 
     def source(self):
-        tools.get(**self.conan_data["sources"][self.version],
-            destination=self._source_subfolder, strip_root=True)
-        for patch in self.conan_data.get("patches", {}).get(self.version, []):
-            tools.patch(**patch)
+        get(self, **self.conan_data["sources"][self.version],
+            destination=self.source_folder, strip_root=True)
+
+    def layout(self):
+        basic_layout(self, src_folder="src")
+
+    def build(self):
+        apply_conandata_patches(self)
 
     def package(self):
-        header_dir = os.path.join(self._source_subfolder, "include")
-        self.copy(pattern="jwt-cpp/**.h", dst="include", src=header_dir, keep_path=True)
-        self.copy("LICENSE", dst="licenses", src=self._source_subfolder)
+        header_dir = os.path.join(self.source_folder, "include", "jwt-cpp")
+        copy(self, pattern="*.h", dst=os.path.join(self.package_folder, "include", "jwt-cpp"), src=header_dir, keep_path=True)
+        copy(self, "LICENSE", dst=os.path.join(self.package_folder,"licenses"), src=self.source_folder)
 
     def package_id(self):
-        self.info.header_only()
+        self.info.clear()
 
     def package_info(self):
         self.cpp_info.set_property("cmake_file_name", "jwt-cpp")
@@ -50,3 +54,11 @@ def package_info(self):
         # TODO: to remove in conan v2 once cmake_find_package* generators removed
         self.cpp_info.names["cmake_find_package"] = "jwt-cpp"
         self.cpp_info.names["cmake_find_package_multi"] = "jwt-cpp"
+
+        if self._supports_generic_json:
+            self.cpp_info.defines.append("JWT_DISABLE_PICOJSON")
+
+        self.cpp_info.bindirs = []
+        self.cpp_info.frameworkdirs = []
+        self.cpp_info.libdirs = []
+        self.cpp_info.resdirs = []


Test patch:
diff --git a/recipes/jwt-cpp/all/test_package/CMakeLists.txt b/recipes/jwt-cpp/all/test_package/CMakeLists.txt
--- a/recipes/jwt-cpp/all/test_package/CMakeLists.txt
+++ b/recipes/jwt-cpp/all/test_package/CMakeLists.txt
@@ -1,12 +1,17 @@
-cmake_minimum_required(VERSION 3.1)
-project(PackageTest CXX)
+cmake_minimum_required(VERSION 3.8)
+project(test_package CXX)
 
-include("${CMAKE_BINARY_DIR}/conanbuildinfo.cmake")
-conan_basic_setup(TARGETS)
+find_package(jwt-cpp REQUIRED CONFIG)
+find_package(picojson REQUIRED CONFIG)
 
-find_package(jwt-cpp REQUIRED)
+add_executable(${PROJECT_NAME} test_package.cpp)
+target_link_libraries(${PROJECT_NAME} PRIVATE jwt-cpp::jwt-cpp picojson::picojson)
+target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_11)
 
-add_executable(example example.cpp)
-set_property(TARGET example PROPERTY CXX_STANDARD 11)
+if (jwt-cpp_VERSION VERSION_GREATER 0.4.0)
+    target_compile_definitions(${PROJECT_NAME} PRIVATE JSON_TRAITS_NEEDED)
+endif()
 
-target_link_libraries(example jwt-cpp::jwt-cpp CONAN_PKG::picojson)
+if (jwt-cpp_VERSION VERSION_GREATER_EQUAL 0.6.0)
+    target_compile_definitions(${PROJECT_NAME} PRIVATE HAS_DEFAULT_TRAITS)
+endif()
diff --git a/recipes/jwt-cpp/all/test_package/conanfile.py b/recipes/jwt-cpp/all/test_package/conanfile.py
--- a/recipes/jwt-cpp/all/test_package/conanfile.py
+++ b/recipes/jwt-cpp/all/test_package/conanfile.py
@@ -1,20 +1,27 @@
-from conans import ConanFile, CMake, tools
+from conan import ConanFile
+from conan.tools.build import can_run
+from conan.tools.cmake import cmake_layout, CMake
 import os
 
 
 class JsonCppTestConan(ConanFile):
     settings = "os", "compiler", "build_type", "arch"
-    generators = "cmake", "cmake_find_package"
+    generators = "CMakeToolchain", "CMakeDeps", "VirtualRunEnv"
+    test_type = "explicit"
 
     def requirements(self):
+        self.requires(self.tested_reference_str)
         self.requires("picojson/1.3.0")
 
+    def layout(self):
+        cmake_layout(self)
+
     def build(self):
         cmake = CMake(self)
         cmake.configure()
         cmake.build()
 
     def test(self):
-        if not tools.cross_building(self):
-            bin_path = os.path.join("bin", "example")
-            self.run(bin_path, run_environment=True)
+        if can_run(self):
+            bin_path = os.path.join(self.cpp.build.bindirs[0], "test_package")
+            self.run(bin_path, env="conanrun")
diff --git a/recipes/jwt-cpp/all/test_package/example.cpp b/recipes/jwt-cpp/all/test_package/test_package.cpp
similarity index 77%
rename from recipes/jwt-cpp/all/test_package/example.cpp
rename to recipes/jwt-cpp/all/test_package/test_package.cpp
--- a/recipes/jwt-cpp/all/test_package/example.cpp
+++ b/recipes/jwt-cpp/all/test_package/test_package.cpp
@@ -1,4 +1,13 @@
 #include <jwt-cpp/jwt.h>
+
+#ifdef JSON_TRAITS_NEEDED
+  #ifndef HAS_DEFAULT_TRAITS
+    #include "traits/defaults.h"
+  #else
+    #include <jwt-cpp/traits/kazuho-picojson/defaults.h>
+  #endif
+#endif
+
 #include <iostream>
 
 int main() {
diff --git a/recipes/jwt-cpp/all/test_package/traits/defaults.h b/recipes/jwt-cpp/all/test_package/traits/defaults.h
new file mode 100644
--- /dev/null
+++ b/recipes/jwt-cpp/all/test_package/traits/defaults.h
@@ -0,0 +1,58 @@
+#ifndef JWT_CPP_KAZUHO_PICOJSON_DEFAULTS_H
+#define JWT_CPP_KAZUHO_PICOJSON_DEFAULTS_H
+
+#include "traits.h"
+
+namespace jwt {
+  /**
+   * \brief a class to store a generic [picojson](https://github.com/kazuho/picojson) value as claim
+   *
+   * This type is the specialization of the \ref basic_claim class which
+   * uses the standard template types.
+   */
+  using claim = basic_claim<traits::kazuho_picojson>;
+
+  /**
+   * Create a verifier using the default clock
+   * \return verifier instance
+   */
+  inline verifier<default_clock, traits::kazuho_picojson> verify() {
+    return verify<default_clock, traits::kazuho_picojson>(default_clock{});
+  }
+
+  /**
+   * Return a builder instance to create a new token
+   */
+  inline builder<traits::kazuho_picojson> create() { return builder<traits::kazuho_picojson>(); }
+
+#ifndef JWT_DISABLE_BASE64
+  /**
+   * Decode a token
+   * \param token Token to decode
+   * \return Decoded token
+   * \throw std::invalid_argument Token is not in correct format
+   * \throw std::runtime_error Base64 decoding failed or invalid json
+   */
+  inline decoded_jwt<traits::kazuho_picojson> decode(const std::string& token) {
+    return decoded_jwt<traits::kazuho_picojson>(token);
+  }
+#endif
+
+  /**
+   * Decode a token
+   * \tparam Decode is callabled, taking a string_type and returns a string_type.
+   * It should ensure the padding of the input and then base64url decode and
+   * return the results.
+   * \param token Token to decode
+   * \param decode The token to parse
+   * \return Decoded token
+   * \throw std::invalid_argument Token is not in correct format
+   * \throw std::runtime_error Base64 decoding failed or invalid json
+   */
+  template<typename Decode>
+  decoded_jwt<traits::kazuho_picojson> decode(const std::string& token, Decode decode) {
+    return decoded_jwt<traits::kazuho_picojson>(token, decode);
+  }
+} // namespace jwt
+
+#endif // JWT_CPP_KAZUHO_PICOJSON_DEFAULTS_H
diff --git a/recipes/jwt-cpp/all/test_package/traits/traits.h b/recipes/jwt-cpp/all/test_package/traits/traits.h
new file mode 100644
--- /dev/null
+++ b/recipes/jwt-cpp/all/test_package/traits/traits.h
@@ -0,0 +1,76 @@
+#ifndef JWT_CPP_PICOJSON_TRAITS_H
+#define JWT_CPP_PICOJSON_TRAITS_H
+
+#ifndef PICOJSON_USE_INT64
+#define PICOJSON_USE_INT64
+#endif
+#include <picojson.h>
+
+#ifndef JWT_DISABLE_PICOJSON
+#define JWT_DISABLE_PICOJSON
+#endif
+#include "jwt-cpp/jwt.h"
+
+namespace jwt {
+  namespace traits {
+    struct kazuho_picojson {
+      using value_type = picojson::value;
+      using object_type = picojson::object;
+      using array_type = picojson::array;
+      using string_type = std::string;
+      using number_type = double;
+      using integer_type = int64_t;
+      using boolean_type = bool;
+
+      static json::type get_type(const picojson::value& val) {
+        using json::type;
+        if (val.is<bool>()) return type::boolean;
+        if (val.is<int64_t>()) return type::integer;
+        if (val.is<double>()) return type::number;
+        if (val.is<std::string>()) return type::string;
+        if (val.is<picojson::array>()) return type::array;
+        if (val.is<picojson::object>()) return type::object;
+
+        throw std::logic_error("invalid type");
+      }
+
+      static picojson::object as_object(const picojson::value& val) {
+        if (!val.is<picojson::object>()) throw std::bad_cast();
+        return val.get<picojson::object>();
+      }
+
+      static std::string as_string(const picojson::value& val) {
+        if (!val.is<std::string>()) throw std::bad_cast();
+        return val.get<std::string>();
+      }
+
+      static picojson::array as_array(const picojson::value& val) {
+        if (!val.is<picojson::array>()) throw std::bad_cast();
+        return val.get<picojson::array>();
+      }
+
+      static int64_t as_int(const picojson::value& val) {
+        if (!val.is<int64_t>()) throw std::bad_cast();
+        return val.get<int64_t>();
+      }
+
+      static bool as_bool(const picojson::value& val) {
+        if (!val.is<bool>()) throw std::bad_cast();
+        return val.get<bool>();
+      }
+
+      static double as_number(const picojson::value& val) {
+        if (!val.is<double>()) throw std::bad_cast();
+        return val.get<double>();
+      }
+
+      static bool parse(picojson::value& val, const std::string& str) {
+        return picojson::parse(val, str).empty();
+      }
+
+      static std::string serialize(const picojson::value& val) { return val.serialize(); }
+    };
+  } // namespace traits
+} // namespace jwt
+
+#endif
diff --git a/recipes/jwt-cpp/all/test_v1_package/CMakeLists.txt b/recipes/jwt-cpp/all/test_v1_package/CMakeLists.txt
new file mode 100644
--- /dev/null
+++ b/recipes/jwt-cpp/all/test_v1_package/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.1)
+project(test_package CXX)
+
+include("${CMAKE_BINARY_DIR}/conanbuildinfo.cmake")
+conan_basic_setup(TARGETS)
+
+find_package(jwt-cpp REQUIRED CONFIG)
+find_package(picojson REQUIRED CONFIG)
+
+add_executable(${PROJECT_NAME} ../test_package/test_package.cpp)
+target_link_libraries(${PROJECT_NAME} PRIVATE jwt-cpp::jwt-cpp picojson::picojson)
+target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_11)
+
+if (jwt-cpp_VERSION VERSION_GREATER 0.4.0)
+    target_compile_definitions(${PROJECT_NAME} PRIVATE JSON_TRAITS_NEEDED)
+endif()
+
+if (jwt-cpp_VERSION VERSION_GREATER_EQUAL 0.6.0)
+    target_compile_definitions(${PROJECT_NAME} PRIVATE HAS_DEFAULT_TRAITS)
+endif()
diff --git a/recipes/jwt-cpp/all/test_v1_package/conanfile.py b/recipes/jwt-cpp/all/test_v1_package/conanfile.py
new file mode 100644
--- /dev/null
+++ b/recipes/jwt-cpp/all/test_v1_package/conanfile.py
@@ -0,0 +1,21 @@
+from conans import ConanFile, CMake
+from conan.tools.build import cross_building
+import os
+
+
+class TestPackageV1Conan(ConanFile):
+    settings = "os", "arch", "compiler", "build_type"
+    generators = "cmake", "cmake_find_package_multi"
+
+    def requirements(self):
+        self.requires("picojson/1.3.0")
+
+    def build(self):
+        cmake = CMake(self)
+        cmake.configure()
+        cmake.build()
+
+    def test(self):
+        if not cross_building(self):
+            bin_path = os.path.join("bin", "test_package")
+            self.run(bin_path, run_environment=True)