From 83fc55aef50516fe01fc11d9c53a51aa04ebcc36 Mon Sep 17 00:00:00 2001 From: PerseoGI Date: Mon, 24 Jun 2024 14:05:12 +0200 Subject: [PATCH 1/6] Added vendored package blog post --- ...24-Introducing-vendoring-packages.markdown | 427 ++++++++++++++++++ .../2024-06-24/standard-sdk-graph.png | Bin 0 -> 21164 bytes .../2024-06-24/vendored-expanded-graph.png | Bin 0 -> 12119 bytes .../2024-06-24/vendored-sdk-graph.png | Bin 0 -> 18687 bytes readme.md | 8 + 5 files changed, 435 insertions(+) create mode 100644 _posts/2024-06-24-Introducing-vendoring-packages.markdown create mode 100644 assets/post_images/2024-06-24/standard-sdk-graph.png create mode 100644 assets/post_images/2024-06-24/vendored-expanded-graph.png create mode 100644 assets/post_images/2024-06-24/vendored-sdk-graph.png diff --git a/_posts/2024-06-24-Introducing-vendoring-packages.markdown b/_posts/2024-06-24-Introducing-vendoring-packages.markdown new file mode 100644 index 00000000..df9f0589 --- /dev/null +++ b/_posts/2024-06-24-Introducing-vendoring-packages.markdown @@ -0,0 +1,427 @@ +--- +layout: post +comments: false +title: "Introducing vendoring packages: Create and share packages decoupled from their dependencies" +meta_title: "(optional) A longer more descriptive title for search engines to index" +description: "A short summary of the post that will be displayed in the search engine results" +keywords: "repackage,repackaging,conan repackage,package_id" +--- + + +We’re excited to roll out a highly anticipated feature that will significantly +enhance how Conan users, including software vendors, manage and distribute +their packages: the "vendor" feature. This new addition aims to streamline the +deployment and sharing process of Conan packages, offering greater control over +internal recipes and binaries **without exposing proprietary details**, or simply +isolating implementation details across organization teams. Let’s dive into +what "vendor" packages bring to the table and how they can benefit your +workflow. + +## What is a "vendored package"? + +The "vendor" feature allows developers to distribute their software through +Conan while keeping internal dependencies and recipes private. By enabling the +vendor attribute in your Conan recipe, you can prevent Conan from downloading +the recipes and binaries of your package's dependencies. This means you can +encapsulate all necessary binaries and libraries within your package, ensuring +that end-users have no access to your internal build details or private +repositories. + +## Key Benefits + +1. **Enhanced Privacy and Security** + + By using the vendor feature, you can share your software packages without + exposing the recipes and binaries of your internal dependencies. This is + crucial for maintaining the confidentiality of proprietary code and internal + build processes. + + +2. **Streamlined Distribution** + + - The vendor feature simplifies the distribution process. Whether you’re using + Conan Center Index or a private artifact repository, pre-built binaries for + various configurations can be included, ensuring that end-users receive a + ready-to-use package without the need for additional downloads + - Vendoring can also be useful inside organizations by allowing sharing + - SDKs between different work groups without sharing internals + +3. **Reduced Build Times** + + When a consumer installs a vendored recipe, Conan won’t download individual + dependency binaries or recipes from the server, potentially saving significant + time and storage space, especially in production environments. + +## Usage example + +For this example, make sure to at least have Conan v2.4.1 installed available. + +1. Create a basic library from the CMake template + + ```sh + $ mkdir vendor-example && cd vendor-example + $ mkdir lib_a && cd lib_a + $ conan new cmake_lib -d name=lib_a -d version=1.0 + $ conan create . + ``` + +2. Create a package depending on the previous library, which will be the one we’ll use to vendor its dependencies + + ```sh + $ cd .. && mkdir sdk && cd sdk + $ conan new cmake_lib -d name=sdk -d version=1.0 -d requires="lib_a/1.0" + $ conan create . + ``` + +3. Create a consumer application depending on the sdk library: + + ```sh + $ cd .. && mkdir app && cd app + $ conan new cmake_exe -d name=app -d version=1.0 -d requires="sdk/1.0" + ``` + +4. Install the created application + + ```sh + $ conan install . --build=missing + + ======== Computing dependency graph ======== + ... + Requirements + lib_a/1.0#ab64452c42599a3dc0ee6a0dc90bbd90 - Cache + sdk/1.0#1cb781c232f63845b7943764d8a084ed - Cache + + ======== Computing necessary packages ======== + Requirements + lib_a/1.0#ab64452c42599a3dc0ee6a0dc90bbd90:39f48664f195e0847f59889d8a4cdfc6bca84bf1#e34a89988cafb2bf67f6adf40b06f442 - Cache + sdk/1.0#1cb781c232f63845b7943764d8a084ed:12ffb661ea06cee312194b5f6acd48e8236b8ed8#9127cf762dfd1a1f505ecd1d3ac056b9 - Cache + ``` + + Transitive dependencies are required as expected. + + +5. Generate the graph to see a later comparison + + ```sh + $ conan graph info . --format=html > graph.html + ``` + +6. Now let's dive into the vendoring feature. Some changes need to be made in the SDK ``conanfile.py``: + - As this example aims to make a vendored **static library**, we should first change the ``package_type`` accordingly + - Set the class attribute ``vendor`` to ``True``. This will enable the vendoring feature + - Remove the unnecessary shared option from options and default_options, and remove configure method because it not needed anymore + - Actually repackage the lib_a library inside the SDK. As we are generating a static library, we can achieve this by copying the ``liblib_a.a`` static library inside the SDK library + - Finally, update the ``cpp_info.libs`` adding the ``lib_a`` dependency for consumers + + ```py + from conan import ConanFile + from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps + from conan.tools.files import copy + import os + + + class sdkRecipe(ConanFile): + name = "sdk" + version = "1.0" + package_type = "static-library" + vendor = True + + # vendor = True + + # Optional metadata + license = "" + author = " " + url = "" + description = "" + topics = ("", "", "") + + # Binary configuration + settings = "os", "compiler", "build_type", "arch" + options = {"fPIC": [True, False]} + default_options = {"fPIC": True} + + # Sources are located in the same place as this recipe, copy them to the recipe + exports_sources = "CMakeLists.txt", "src/*", "include/*" + + def config_options(self): + if self.settings.os == "Windows": + self.options.rm_safe("fPIC") + + def layout(self): + cmake_layout(self) + + def requirements(self): + self.requires("lib_a/1.0") + + def generate(self): + deps = CMakeDeps(self) + deps.generate() + tc = CMakeToolchain(self) + tc.generate() + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + # Repackage static dependencies inside the package + copy(self, "*.a", src=self.dependencies["lib_a"].cpp_info.libdir, dst=os.path.join(self.package_folder, self.cpp_info.libdir)) + cmake = CMake(self) + cmake.install() + + def package_info(self): + self.cpp_info.libs = ["sdk", "lib_a"] + + ``` + +7. Apply the changes and create the SDK package: + + ```sh + $ cd ../sdk + $ conan create . + ``` + +8. Finally, let’s reinstall the application again. + + ```sh + $ cd ../app + $ conan install . --build=missing + + ======== Computing dependency graph ======== + Graph root + ... + Requirements + sdk/1.0#f2fd2a793849725303073d37b15042b2 - Cache + + ======== Computing necessary packages ======== + Requirements + sdk/1.0#f2fd2a793849725303073d37b15042b2:5d605f63db975c8c6004cc0a0b5c99c99dce6cc3#92c538ec767c2ff02a2fddf6b4106d02 - Cache + ``` + + As it can be seen, while computing the dependency graph, conan does not + retrieve either the recipe of ``lib_a/1.0`` nor the binaries. + +9. We could try to even remove ``lib_a`` from our local cache and install again the application: + + ```sh + $ conan remove "lib_a*" -c + Found 1 pkg/version recipes matching lib_a* in local cache + Remove summary: + Local Cache + lib_a/1.0#ab64452c42599a3dc0ee6a0dc90bbd90: Removed recipe and all binaries + ``` + + ```sh + $ conan install . + ... + Requirements + sdk/1.0#f2fd2a793849725303073d37b15042b2 - Cache + ... + Install finished successfully + ``` + + It works!!! Here is where vendoring feature shines. Package creators can + distribute their packages without needing to distribute their private + dependencies. + + +10. Generate graph info of the application using vendored SDK + + ```sh + $ conan graph info . --format=html > vendored-graph.html + ``` + + Comparison between graphs: + +
+
+ Standard sdk graph + Standard sdk graph +
+
+ Vendored sdk graph + Vendored sdk graph +
+
+
+ + **Note**: Red dashed borders mean the package is vendoring its dependencies + +## How should the “vendor” feature be used? + +For releasing a vendored package, the creator should follow these steps: + +1. **Encapsulation of dependencies** + + Ensure that all dependencies are correctly repackaged inside the vendoring + package. This involves encapsulating binaries, static libraries, and shared + libraries within the vendoring domain. Users may use the vendoring package with + different kinds of environments, dependencies, shared libraries enabled, etc. + The package creator is responsible for ensuring no conflicts should surface. + +2. **Distribution of vendoring packages** + + When distributing a vendoring package, pre-built binaries should be generated + for the different configurations your consumers might need. That way, users + could use the vendored package without needing to compile it from scratch and + without the need to have direct access to the package dependencies (which may + be private for the organization) + + +## Advanced details + +### Dependency graph and its importance + +To understand the inner workings of the "vendor" feature, it is essential to +grasp the concept of a dependency graph in Conan. When Conan builds the +dependency graph for a package, it downloads the recipes and binaries for all +dependencies, constructing a detailed map of all relationships and +configurations involved. + +The dependency graph is a critical component in Conan, representing how +packages depend on each other. It ensures that all necessary binaries and +libraries are correctly resolved and compatible. Typically, Conan expands this +graph fully, downloading all the recipes and binaries involved. However, with +the vendor feature enabled, you can limit this expansion. + +### Limiting Graph Expansion + +By enabling the vendor option, the consumer of the recipe is instructed not to +expand the dependency graph beyond the vendored package. This means Conan won't +download the recipes or binaries of internal dependencies, keeping the build +process lean and secure. This feature is particularly useful for: + +- **Encapsulation**: Keeping private dependencies hidden and secure. +- **Efficiency**: Reducing download and build times by not fetching unnecessary components. +- **Control**: Allowing software vendors to manage how their packages are used and distributed without exposing internal details. + +### Forcing the build of a vendoring package + +We have seen how a vendor can be used from a consumer directly downloading +binaries without needing to download any dependency data. + +But what happens if we want to compile the internal vendored dependencies? + +1. As we have previously deleted ``lib_a`` package, we should re create it: + + ```sh + $ cd ../lib_a && conan create . + ``` + Now, let’s force the build of our previous SDK example: + + ```sh + $ cd ../app + $ conan install . --build="sdk/1.0" + ... + ======== Computing necessary packages ======== + sdk/1.0: Forced build from source + Requirements + sdk/1.0#f2fd2a793849725303073d37b15042b2:5d605f63db975c8c6004cc0a0b5c99c99dce6cc3 - Invalid + ERROR: There are invalid packages: + sdk/1.0: Invalid: The package 'sdk/1.0' is a vendoring one, needs to be built from source, but it didn't enable 'tools.graph:vendor=build' to compute its dependencies + ``` + + Trying to build a vendor package will fail by default unless setting the ``tools.graph:vendor`` configuration to “build”. + +2. Once the ``vendor`` configuration is enabled, the user must have access to packaged dependencies as if it were a normal package. + + ```sh + $ conan install . --build="sdk/1.0" -c tools.graph:vendor=build + ... + ======== Computing necessary packages ======== + sdk/1.0: Forced build from source + Requirements + lib_a/1.0#ab64452c42599a3dc0ee6a0dc90bbd90:39f48664f195e0847f59889d8a4cdfc6bca84bf1#5a8bfa1c980c2008e7e24996a4b48477 - Cache + sdk/1.0#f2fd2a793849725303073d37b15042b2:5d605f63db975c8c6004cc0a0b5c99c99dce6cc3 - Build + + ======== Installing packages ======== + lib_a/1.0: Already installed! (1 of 2) + ... + Install finished successfully + ``` + + When forcing the compilation of a vendored dependency, the graph expands again, + revealing the encapsulated dependencies necessary for the build. + +3. Let's generate the graph again to see how the expansion works: + + ```sh + $ conan graph info . --build="sdk/1.0" -c tools.graph:vendor=build --format=html > vendored-expanded-graph.html + ``` + +
+ Vendored expanded graph + Vendored expanded graph +
+
+ + **Note**: Red dashed border means the package is a vendor and yellow background means the package has been forced to be built + +4. To verify a vendored package does not need to have their transitive + dependencies accessible unless forced to build, we can try to remove our ``lib_a`` package from our local cache and install again: + + ```sh + $ conan remove "lib_a*" -c + Found 1 pkg/version recipes matching lib_a* in local cache + Remove summary: + Local Cache + lib_a/1.0#ab64452c42599a3dc0ee6a0dc90bbd90: Removed recipe and all binaries + ``` + + ```sh + $ conan install . --build="sdk/1.0" -c tools.graph:vendor=build + ... + ======== Computing dependency graph ======== + lib_a/1.0: Not found in local cache, looking in remotes... + lib_a/1.0: Checking remote: conancenter + ... + Requirements + sdk/1.0#f2fd2a793849725303073d37b15042b2 - Cache + ERROR: Package 'lib_a/1.0' not resolved: Unable to find 'lib_a/1.0' in remotes. + ``` + + As expected, an error package not resolved is thrown when forcing building the vendored package if we do not have access to its dependencies. + + +### Proxy vendor for Extra Privacy + +For vendors that want to further obscure their dependency names and versions, a +proxy vendored package can be created. This proxy package should include all +internal/private dependencies and should be made inaccessible to clients. + +The primary vendored recipe depends on this proxy package, and by marking both +packages as vendored, the actual dependencies and their versions will be hidden +from the end-user. + +### Impact on Package ID Calculation + +To support the vendor feature, we’ve adjusted how the ``package_id`` is calculated +for vendored packages. Since dependencies and their versions are invisible to +the end-user, they are excluded from the ``package_id`` calculation. Only the +recipe revision will change with recipe content changes, but the ``package_id`` +will remain the same. This ensures that any kind of change to internal +dependencies (updating, adding or removing) will not alter the ``package_id``, +saving time and effort in environments where consistent package IDs are +crucial. + +It goes without saying that the provider in charge of packaging the package +should be in charge of updating the version of the package when any of its +internal dependencies change version in order to update the ``package_id`` and let +consumers know that the package has changed in some way. + +## Conclusion + +The "vendoring" feature is a powerful tool for any organization looking to +distribute software through Conan while maintaining control over internal +dependencies. By encapsulating and securing your binaries, you can ensure a +smooth and efficient distribution process, enhanced privacy, and significant +time savings. We’re thrilled to bring this feature to the community and look +forward to seeing how it enhances your workflows. + +On the other side, we recommend avoiding abuse of this new feature, for +example, we do not consider that it has any place in ``conan-center-index`` or in +other remotes, except perhaps for some application type package (tool +requires). + +Stay tuned for more updates and, as always, happy packaging with Conan! diff --git a/assets/post_images/2024-06-24/standard-sdk-graph.png b/assets/post_images/2024-06-24/standard-sdk-graph.png new file mode 100644 index 0000000000000000000000000000000000000000..cd69867ef95683af8abb8315d52d532e2e96790f GIT binary patch literal 21164 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 H7NP$Kz&(vx literal 0 HcmV?d00001 diff --git a/assets/post_images/2024-06-24/vendored-expanded-graph.png b/assets/post_images/2024-06-24/vendored-expanded-graph.png new file mode 100644 index 0000000000000000000000000000000000000000..30173f1a504ac15605f9d72dadc0f289f072a83b GIT binary patch literal 12119 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 b^^Bb8Iw_-F^X>Mp|5D3Kt4LKyd<^^_+XrN# literal 0 HcmV?d00001 diff --git a/assets/post_images/2024-06-24/vendored-sdk-graph.png b/assets/post_images/2024-06-24/vendored-sdk-graph.png new file mode 100644 index 0000000000000000000000000000000000000000..18546d63ff495ae6d79b0dc0ee622650a493a0c6 GIT binary patch literal 18687 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 B4dVa+ literal 0 HcmV?d00001 diff --git a/readme.md b/readme.md index aac40be8..1a0f3412 100644 --- a/readme.md +++ b/readme.md @@ -16,6 +16,14 @@ until the publication date: jekyll serve _config.yml --watch --future ``` +- Preview in local: Docker + +```sh +docker run --rm -v $(pwd):/srv/jekyll -p 4000:4000 jekyll/jekyll:latest sh -c "gem install webrick && jekyll serve --host 0.0.0.0" +``` + +See blog at localhost:4000 + - Create new post: Just create the file with the format YEAR-MONTH-DAY-title.md ## Post Front Matter From ad825e4db4645fce472b7acd623b4a07e1a19755 Mon Sep 17 00:00:00 2001 From: PerseoGI Date: Mon, 24 Jun 2024 14:59:01 +0200 Subject: [PATCH 2/6] Fixed threads --- ...24-Introducing-vendoring-packages.markdown | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/_posts/2024-06-24-Introducing-vendoring-packages.markdown b/_posts/2024-06-24-Introducing-vendoring-packages.markdown index df9f0589..bca0a54a 100644 --- a/_posts/2024-06-24-Introducing-vendoring-packages.markdown +++ b/_posts/2024-06-24-Introducing-vendoring-packages.markdown @@ -3,15 +3,15 @@ layout: post comments: false title: "Introducing vendoring packages: Create and share packages decoupled from their dependencies" meta_title: "(optional) A longer more descriptive title for search engines to index" -description: "A short summary of the post that will be displayed in the search engine results" -keywords: "repackage,repackaging,conan repackage,package_id" +description: "Explore the capabilities of Conan's vendor feature and how can be used in a enterprise environment." +keywords: "vendoring,package,repackaging,conan vendor,package_id,private" --- We’re excited to roll out a highly anticipated feature that will significantly enhance how Conan users, including software vendors, manage and distribute their packages: the "vendor" feature. This new addition aims to streamline the -deployment and sharing process of Conan packages, offering greater control over +deployment and sharing processes of Conan packages, offering greater control over internal recipes and binaries **without exposing proprietary details**, or simply isolating implementation details across organization teams. Let’s dive into what "vendor" packages bring to the table and how they can benefit your @@ -39,7 +39,7 @@ repositories. 2. **Streamlined Distribution** - - The vendor feature simplifies the distribution process. Whether you’re using + - The vendor feature simplifies the distribution process. Whether you are using Conan Center Index or a private artifact repository, pre-built binaries for various configurations can be included, ensuring that end-users receive a ready-to-use package without the need for additional downloads @@ -65,7 +65,7 @@ For this example, make sure to at least have Conan v2.4.1 installed available. $ conan create . ``` -2. Create a package depending on the previous library, which will be the one we’ll use to vendor its dependencies +2. Create a package depending on the previous library, which will be the one we will use to vendor its dependencies ```sh $ cd .. && mkdir sdk && cd sdk @@ -356,9 +356,9 @@ But what happens if we want to compile the internal vendored dependencies?
- **Note**: Red dashed border means the package is a vendor and yellow background means the package has been forced to be built + **Note**: Red dashed borders means the package is a vendor and yellow background means the package has been forced to be built -4. To verify a vendored package does not need to have their transitive +4. To verify that a vendored package does not need to have their transitive dependencies accessible unless forced to build, we can try to remove our ``lib_a`` package from our local cache and install again: ```sh @@ -381,7 +381,9 @@ But what happens if we want to compile the internal vendored dependencies? ERROR: Package 'lib_a/1.0' not resolved: Unable to find 'lib_a/1.0' in remotes. ``` - As expected, an error package not resolved is thrown when forcing building the vendored package if we do not have access to its dependencies. + As expected, an "Error: Package 'lib_a/1.0' not resolved" is raised when + forcing the build of the vendored package if we do not have access to its + dependencies. ### Proxy vendor for Extra Privacy @@ -405,10 +407,10 @@ dependencies (updating, adding or removing) will not alter the ``package_id``, saving time and effort in environments where consistent package IDs are crucial. -It goes without saying that the provider in charge of packaging the package +It goes without saying that the provider of packaging the package should be in charge of updating the version of the package when any of its -internal dependencies change version in order to update the ``package_id`` and let -consumers know that the package has changed in some way. +internal dependencies change version in order let consumers know that the +package has changed in some way. ## Conclusion From 8f36579e50623bee693b6cf059781b003bb170cd Mon Sep 17 00:00:00 2001 From: PerseoGI Date: Wed, 3 Jul 2024 12:58:54 +0200 Subject: [PATCH 3/6] Added line highlight custom plugins and highlighted blog patch --- _plugins/inline_highlight.rb | 27 + _plugins/line_highlighting.rb | 29 + _plugins/md_hl_syntax_to_liquid.rb | 22 + ...24-Introducing-vendoring-packages.markdown | 528 +++++++++--------- css/style.css | 5 + readme.md | 15 + 6 files changed, 360 insertions(+), 266 deletions(-) create mode 100644 _plugins/inline_highlight.rb create mode 100644 _plugins/line_highlighting.rb create mode 100644 _plugins/md_hl_syntax_to_liquid.rb diff --git a/_plugins/inline_highlight.rb b/_plugins/inline_highlight.rb new file mode 100644 index 00000000..e0ce23a7 --- /dev/null +++ b/_plugins/inline_highlight.rb @@ -0,0 +1,27 @@ +# jekyll_inline_highlight +# +# A Liquid tag for inline syntax highlighting in Jekyll +# +# https://github.com/bdesham/inline_highlight +# +# Copyright (c) 2014-2015, Tom Preston-Werner and Benjamin Esham +# See README.md for full copyright information. + +module Jekyll + class InlineHighlightBlock < Tags::HighlightBlock + + def add_code_tag(code) + code_attributes = [ + "class=\"highlight language-#{@lang.to_s.gsub("+", "-")}\"", + "data-lang=\"#{@lang.to_s}\"" + ].join(" ") + "#{code.chomp.strip}" + end + + def render(context) + super.strip + end + end +end + +Liquid::Template.register_tag("ihighlight", Jekyll::InlineHighlightBlock) diff --git a/_plugins/line_highlighting.rb b/_plugins/line_highlighting.rb new file mode 100644 index 00000000..7a5812f3 --- /dev/null +++ b/_plugins/line_highlighting.rb @@ -0,0 +1,29 @@ +# Monkey-patch to allow highlighting lines +# PerseoGI: this is provisional until Jekyll 4.4 gets released which incorporates this feature +# Used same parameter (mark_lines) as in https://github.com/jekyll/jekyll/pull/9138 +# +# Notice a `.hll` style must be defined in your CSS to highlight the lines +module Jekyll + module Tags + class HighlightBlock + def render_rouge(code) + require "rouge" + + formatter = Rouge::Formatters::HTMLLineHighlighter.new( + ::Rouge::Formatters::HTML.new, + highlight_lines: parse_highlighted_lines(@highlight_options[:mark_lines]) + ) + lexer = ::Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText + formatter.format(lexer.lex(code)) + end + + private + + def parse_highlighted_lines(lines_string) + return [] if lines_string.nil? + + lines_string.map(&:to_i) + end + end + end +end diff --git a/_plugins/md_hl_syntax_to_liquid.rb b/_plugins/md_hl_syntax_to_liquid.rb new file mode 100644 index 00000000..a46c4dfb --- /dev/null +++ b/_plugins/md_hl_syntax_to_liquid.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +# Convert markdown highlight lines feature syntax to liquid syntax +# Author: https://github.com/jekyll/jekyll/issues/8621#issuecomment-839184339 +# Usage: +# ```py{1 2-3} +# def foo(): +# print("Hello World") +# a = 42 +# ``` + +Jekyll::Hooks.register(:documents, :pre_render) do |document, payload| + docExt = document.extname.tr(".", "") + + # only process if we deal with a markdown file + if payload["site"]["markdown_ext"].include?(docExt) + document.content.gsub!( + /^\`\`\`([A-z]+){([\d\s]+)}$(.*?)^\`\`\`$/im, + "{% highlight \\1 mark_lines=\"\\2\" %}\\3{% endhighlight %}" + ) + end +end diff --git a/_posts/2024-06-24-Introducing-vendoring-packages.markdown b/_posts/2024-06-24-Introducing-vendoring-packages.markdown index bca0a54a..c225241b 100644 --- a/_posts/2024-06-24-Introducing-vendoring-packages.markdown +++ b/_posts/2024-06-24-Introducing-vendoring-packages.markdown @@ -56,197 +56,193 @@ repositories. For this example, make sure to at least have Conan v2.4.1 installed available. -1. Create a basic library from the CMake template + 1. Create a basic library from the CMake template + +```sh +$ mkdir vendor-example && cd vendor-example +$ mkdir lib_a && cd lib_a +$ conan new cmake_lib -d name=lib_a -d version=1.0 +$ conan create . +``` + + 2. Create a package depending on the previous library, which will be the one we will use to vendor its dependencies + +```sh +$ cd .. && mkdir sdk && cd sdk +$ conan new cmake_lib -d name=sdk -d version=1.0 -d requires="lib_a/1.0" +$ conan create . +``` + + 3. Create a consumer application depending on the sdk library: + +```sh +$ cd .. && mkdir app && cd app +$ conan new cmake_exe -d name=app -d version=1.0 -d requires="sdk/1.0" +``` + + 4. Install the created application + +```sh +$ conan install . --build=missing + +======== Computing dependency graph ======== +... +Requirements + lib_a/1.0#ab64452c42599a3dc0ee6a0dc90bbd90 - Cache + sdk/1.0#1cb781c232f63845b7943764d8a084ed - Cache + +======== Computing necessary packages ======== +Requirements + lib_a/1.0#ab64452c42599a3dc0ee6a0dc90bbd90:39f48664f195e0847f59889d8a4cdfc6bca84bf1#e34a89988cafb2bf67f6adf40b06f442 - Cache + sdk/1.0#1cb781c232f63845b7943764d8a084ed:12ffb661ea06cee312194b5f6acd48e8236b8ed8#9127cf762dfd1a1f505ecd1d3ac056b9 - Cache +``` + +Transitive dependencies are required as expected. + + + 5. Generate the graph to see a later comparison + +```sh +$ conan graph info . --format=html > graph.html +``` + + 6. Now let's dive into the vendoring feature. Some changes need to be made in the SDK ``conanfile.py``: +- As this example aims to make a vendored **static library**, we should first change the ``package_type`` accordingly +- Set the class attribute ``vendor`` to ``True``. This will enable the vendoring feature +- Remove the unnecessary shared option from options and default_options, and remove configure method because it not needed anymore +- Actually repackage the lib_a library inside the SDK. As we are generating a static library, we can achieve this by copying the ``liblib_a.a`` static library inside the SDK library +- Finally, update the ``cpp_info.libs`` adding the ``lib_a`` dependency for consumers + +```py{8 9 21 22 50 55} +from conan import ConanFile +from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps +from conan.tools.files import copy +import os + +class sdkRecipe(ConanFile): + name = "sdk" + version = "1.0" + package_type = "static-library" + vendor = True + + # Optional metadata + license = "" + author = " " + url = "" + description = "" + topics = ("", "", "") + + # Binary configuration + settings = "os", "compiler", "build_type", "arch" + options = {"fPIC": [True, False]} + default_options = {"fPIC": True} + + # Sources are located in the same place as this recipe, copy them to the recipe + exports_sources = "CMakeLists.txt", "src/*", "include/*" + + def config_options(self): + if self.settings.os == "Windows": + self.options.rm_safe("fPIC") + + def layout(self): + cmake_layout(self) + + def requirements(self): + self.requires("lib_a/1.0") + + def generate(self): + deps = CMakeDeps(self) + deps.generate() + tc = CMakeToolchain(self) + tc.generate() + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + # Repackage static dependencies inside the package + copy(self, "*.a", src=self.dependencies["lib_a"].cpp_info.libdir, dst=os.path.join(self.package_folder, self.cpp_info.libdir)) + cmake = CMake(self) + cmake.install() + + def package_info(self): + self.cpp_info.libs = ["sdk", "lib_a"] +``` + + 7. Apply the changes and create the SDK package: - ```sh - $ mkdir vendor-example && cd vendor-example - $ mkdir lib_a && cd lib_a - $ conan new cmake_lib -d name=lib_a -d version=1.0 - $ conan create . - ``` +```sh +$ cd ../sdk +$ conan create . +``` + + 8. Finally, let’s reinstall the application again. + +```sh +$ cd ../app +$ conan install . --build=missing + +======== Computing dependency graph ======== +Graph root +... +Requirements + sdk/1.0#f2fd2a793849725303073d37b15042b2 - Cache + +======== Computing necessary packages ======== +Requirements + sdk/1.0#f2fd2a793849725303073d37b15042b2:5d605f63db975c8c6004cc0a0b5c99c99dce6cc3#92c538ec767c2ff02a2fddf6b4106d02 - Cache +``` + +As it can be seen, while computing the dependency graph, conan does not +retrieve either the recipe of ``lib_a/1.0`` nor the binaries. + + 9. We could try to even remove ``lib_a`` from our local cache and install again the application: + +```sh +$ conan remove "lib_a*" -c +Found 1 pkg/version recipes matching lib_a* in local cache +Remove summary: +Local Cache + lib_a/1.0#ab64452c42599a3dc0ee6a0dc90bbd90: Removed recipe and all binaries +``` + +```sh +$ conan install . +... +Requirements + sdk/1.0#f2fd2a793849725303073d37b15042b2 - Cache +... +Install finished successfully +``` + +It works!!! Here is where vendoring feature shines. Package creators can +distribute their packages without needing to distribute their private +dependencies. + + + 10. Generate graph info of the application using vendored SDK + +```sh +$ conan graph info . --format=html > vendored-graph.html +``` + +Comparison between graphs: -2. Create a package depending on the previous library, which will be the one we will use to vendor its dependencies - - ```sh - $ cd .. && mkdir sdk && cd sdk - $ conan new cmake_lib -d name=sdk -d version=1.0 -d requires="lib_a/1.0" - $ conan create . - ``` - -3. Create a consumer application depending on the sdk library: - - ```sh - $ cd .. && mkdir app && cd app - $ conan new cmake_exe -d name=app -d version=1.0 -d requires="sdk/1.0" - ``` - -4. Install the created application - - ```sh - $ conan install . --build=missing - - ======== Computing dependency graph ======== - ... - Requirements - lib_a/1.0#ab64452c42599a3dc0ee6a0dc90bbd90 - Cache - sdk/1.0#1cb781c232f63845b7943764d8a084ed - Cache - - ======== Computing necessary packages ======== - Requirements - lib_a/1.0#ab64452c42599a3dc0ee6a0dc90bbd90:39f48664f195e0847f59889d8a4cdfc6bca84bf1#e34a89988cafb2bf67f6adf40b06f442 - Cache - sdk/1.0#1cb781c232f63845b7943764d8a084ed:12ffb661ea06cee312194b5f6acd48e8236b8ed8#9127cf762dfd1a1f505ecd1d3ac056b9 - Cache - ``` - - Transitive dependencies are required as expected. - - -5. Generate the graph to see a later comparison - - ```sh - $ conan graph info . --format=html > graph.html - ``` - -6. Now let's dive into the vendoring feature. Some changes need to be made in the SDK ``conanfile.py``: - - As this example aims to make a vendored **static library**, we should first change the ``package_type`` accordingly - - Set the class attribute ``vendor`` to ``True``. This will enable the vendoring feature - - Remove the unnecessary shared option from options and default_options, and remove configure method because it not needed anymore - - Actually repackage the lib_a library inside the SDK. As we are generating a static library, we can achieve this by copying the ``liblib_a.a`` static library inside the SDK library - - Finally, update the ``cpp_info.libs`` adding the ``lib_a`` dependency for consumers - - ```py - from conan import ConanFile - from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps - from conan.tools.files import copy - import os - - - class sdkRecipe(ConanFile): - name = "sdk" - version = "1.0" - package_type = "static-library" - vendor = True - - # vendor = True - - # Optional metadata - license = "" - author = " " - url = "" - description = "" - topics = ("", "", "") - - # Binary configuration - settings = "os", "compiler", "build_type", "arch" - options = {"fPIC": [True, False]} - default_options = {"fPIC": True} - - # Sources are located in the same place as this recipe, copy them to the recipe - exports_sources = "CMakeLists.txt", "src/*", "include/*" - - def config_options(self): - if self.settings.os == "Windows": - self.options.rm_safe("fPIC") - - def layout(self): - cmake_layout(self) - - def requirements(self): - self.requires("lib_a/1.0") - - def generate(self): - deps = CMakeDeps(self) - deps.generate() - tc = CMakeToolchain(self) - tc.generate() - - def build(self): - cmake = CMake(self) - cmake.configure() - cmake.build() - - def package(self): - # Repackage static dependencies inside the package - copy(self, "*.a", src=self.dependencies["lib_a"].cpp_info.libdir, dst=os.path.join(self.package_folder, self.cpp_info.libdir)) - cmake = CMake(self) - cmake.install() - - def package_info(self): - self.cpp_info.libs = ["sdk", "lib_a"] - - ``` - -7. Apply the changes and create the SDK package: - - ```sh - $ cd ../sdk - $ conan create . - ``` - -8. Finally, let’s reinstall the application again. - - ```sh - $ cd ../app - $ conan install . --build=missing - - ======== Computing dependency graph ======== - Graph root - ... - Requirements - sdk/1.0#f2fd2a793849725303073d37b15042b2 - Cache - - ======== Computing necessary packages ======== - Requirements - sdk/1.0#f2fd2a793849725303073d37b15042b2:5d605f63db975c8c6004cc0a0b5c99c99dce6cc3#92c538ec767c2ff02a2fddf6b4106d02 - Cache - ``` - - As it can be seen, while computing the dependency graph, conan does not - retrieve either the recipe of ``lib_a/1.0`` nor the binaries. - -9. We could try to even remove ``lib_a`` from our local cache and install again the application: - - ```sh - $ conan remove "lib_a*" -c - Found 1 pkg/version recipes matching lib_a* in local cache - Remove summary: - Local Cache - lib_a/1.0#ab64452c42599a3dc0ee6a0dc90bbd90: Removed recipe and all binaries - ``` - - ```sh - $ conan install . - ... - Requirements - sdk/1.0#f2fd2a793849725303073d37b15042b2 - Cache - ... - Install finished successfully - ``` - - It works!!! Here is where vendoring feature shines. Package creators can - distribute their packages without needing to distribute their private - dependencies. - - -10. Generate graph info of the application using vendored SDK - - ```sh - $ conan graph info . --format=html > vendored-graph.html - ``` - - Comparison between graphs: - -
-
- Standard sdk graph - Standard sdk graph -
-
- Vendored sdk graph - Vendored sdk graph -
+
+
+ Standard sdk graph + Standard sdk graph +
+
+ Vendored sdk graph + Vendored sdk graph
-
+
+
- **Note**: Red dashed borders mean the package is vendoring its dependencies +**Note**: Red dashed borders mean the package is vendoring its dependencies ## How should the “vendor” feature be used? @@ -303,87 +299,87 @@ binaries without needing to download any dependency data. But what happens if we want to compile the internal vendored dependencies? -1. As we have previously deleted ``lib_a`` package, we should re create it: - - ```sh - $ cd ../lib_a && conan create . - ``` - Now, let’s force the build of our previous SDK example: - - ```sh - $ cd ../app - $ conan install . --build="sdk/1.0" - ... - ======== Computing necessary packages ======== - sdk/1.0: Forced build from source - Requirements - sdk/1.0#f2fd2a793849725303073d37b15042b2:5d605f63db975c8c6004cc0a0b5c99c99dce6cc3 - Invalid - ERROR: There are invalid packages: - sdk/1.0: Invalid: The package 'sdk/1.0' is a vendoring one, needs to be built from source, but it didn't enable 'tools.graph:vendor=build' to compute its dependencies - ``` - - Trying to build a vendor package will fail by default unless setting the ``tools.graph:vendor`` configuration to “build”. - -2. Once the ``vendor`` configuration is enabled, the user must have access to packaged dependencies as if it were a normal package. - - ```sh - $ conan install . --build="sdk/1.0" -c tools.graph:vendor=build - ... - ======== Computing necessary packages ======== - sdk/1.0: Forced build from source - Requirements - lib_a/1.0#ab64452c42599a3dc0ee6a0dc90bbd90:39f48664f195e0847f59889d8a4cdfc6bca84bf1#5a8bfa1c980c2008e7e24996a4b48477 - Cache - sdk/1.0#f2fd2a793849725303073d37b15042b2:5d605f63db975c8c6004cc0a0b5c99c99dce6cc3 - Build - - ======== Installing packages ======== - lib_a/1.0: Already installed! (1 of 2) - ... - Install finished successfully - ``` - - When forcing the compilation of a vendored dependency, the graph expands again, - revealing the encapsulated dependencies necessary for the build. - -3. Let's generate the graph again to see how the expansion works: - - ```sh - $ conan graph info . --build="sdk/1.0" -c tools.graph:vendor=build --format=html > vendored-expanded-graph.html - ``` - -
- Vendored expanded graph - Vendored expanded graph -
-
- - **Note**: Red dashed borders means the package is a vendor and yellow background means the package has been forced to be built - -4. To verify that a vendored package does not need to have their transitive + 1. As we have previously deleted ``lib_a`` package, we should re create it: + +```sh +$ cd ../lib_a && conan create . +``` +Now, let’s force the build of our previous SDK example: + +```sh +$ cd ../app +$ conan install . --build="sdk/1.0" +... +======== Computing necessary packages ======== +sdk/1.0: Forced build from source +Requirements + sdk/1.0#f2fd2a793849725303073d37b15042b2:5d605f63db975c8c6004cc0a0b5c99c99dce6cc3 - Invalid +ERROR: There are invalid packages: +sdk/1.0: Invalid: The package 'sdk/1.0' is a vendoring one, needs to be built from source, but it didn't enable 'tools.graph:vendor=build' to compute its dependencies +``` + +Trying to build a vendor package will fail by default unless setting the ``tools.graph:vendor`` configuration to “build”. + + 2. Once the ``vendor`` configuration is enabled, the user must have access to packaged dependencies as if it were a normal package. + +```sh +$ conan install . --build="sdk/1.0" -c tools.graph:vendor=build +... +======== Computing necessary packages ======== +sdk/1.0: Forced build from source +Requirements + lib_a/1.0#ab64452c42599a3dc0ee6a0dc90bbd90:39f48664f195e0847f59889d8a4cdfc6bca84bf1#5a8bfa1c980c2008e7e24996a4b48477 - Cache + sdk/1.0#f2fd2a793849725303073d37b15042b2:5d605f63db975c8c6004cc0a0b5c99c99dce6cc3 - Build + +======== Installing packages ======== +lib_a/1.0: Already installed! (1 of 2) +... +Install finished successfully +``` + +When forcing the compilation of a vendored dependency, the graph expands again, +revealing the encapsulated dependencies necessary for the build. + + 3. Let's generate the graph again to see how the expansion works: + +```sh +$ conan graph info . --build="sdk/1.0" -c tools.graph:vendor=build --format=html > vendored-expanded-graph.html +``` + +
+ Vendored expanded graph + Vendored expanded graph +
+
+ +**Note**: Red dashed borders means the package is a vendor and yellow background means the package has been forced to be built + + 4. To verify that a vendored package does not need to have their transitive dependencies accessible unless forced to build, we can try to remove our ``lib_a`` package from our local cache and install again: - ```sh - $ conan remove "lib_a*" -c - Found 1 pkg/version recipes matching lib_a* in local cache - Remove summary: - Local Cache - lib_a/1.0#ab64452c42599a3dc0ee6a0dc90bbd90: Removed recipe and all binaries - ``` - - ```sh - $ conan install . --build="sdk/1.0" -c tools.graph:vendor=build - ... - ======== Computing dependency graph ======== - lib_a/1.0: Not found in local cache, looking in remotes... - lib_a/1.0: Checking remote: conancenter - ... - Requirements - sdk/1.0#f2fd2a793849725303073d37b15042b2 - Cache - ERROR: Package 'lib_a/1.0' not resolved: Unable to find 'lib_a/1.0' in remotes. - ``` - - As expected, an "Error: Package 'lib_a/1.0' not resolved" is raised when - forcing the build of the vendored package if we do not have access to its - dependencies. +```sh +$ conan remove "lib_a*" -c +Found 1 pkg/version recipes matching lib_a* in local cache +Remove summary: +Local Cache + lib_a/1.0#ab64452c42599a3dc0ee6a0dc90bbd90: Removed recipe and all binaries +``` + +```sh +$ conan install . --build="sdk/1.0" -c tools.graph:vendor=build +... +======== Computing dependency graph ======== +lib_a/1.0: Not found in local cache, looking in remotes... +lib_a/1.0: Checking remote: conancenter +... +Requirements + sdk/1.0#f2fd2a793849725303073d37b15042b2 - Cache +ERROR: Package 'lib_a/1.0' not resolved: Unable to find 'lib_a/1.0' in remotes. +``` + +As expected, an "Error: Package 'lib_a/1.0' not resolved" is raised when +forcing the build of the vendored package if we do not have access to its +dependencies. ### Proxy vendor for Extra Privacy diff --git a/css/style.css b/css/style.css index a4d39fb3..9cf09d6d 100644 --- a/css/style.css +++ b/css/style.css @@ -877,3 +877,8 @@ body .month-content h2 { text-decoration: underline; } +/* +* Style to be used for highlighting specific lines on a code block +* We are using a custom plugin because until 4.4 release, native support for highlighting specific lines is not available +*/ +.hll { background-color: #ffffcc } diff --git a/readme.md b/readme.md index 1a0f3412..412daddf 100644 --- a/readme.md +++ b/readme.md @@ -45,3 +45,18 @@ keywords: "(optional) comma, separated, values, for seo optimization" > **Note**: SEO Structured Data - This is was attempted but is currently undefined. It was previously done with [TechArticle](https://schema.org/TechArticle) however that probably is not list by [Google's supported list](https://developers.google.com/search/docs/appearance/structured-data/article) + +## Miscellany + +### Highlight specific lines on a code block + +As Jekyll 4.4 has not been released yet, some custom plugins have been added to provide that functionality: + +- line_highlighting.rb: will provide a liquid API to highlight lines on code block emulating native [not yet released functionality](https://jekyllrb.com/docs/liquid/tags/#marking-specific-lines) +- md_hl_syntax_to_liquid.rb: provide a kramdown API to previous liquid syntax +- inline_highlight.rb: plugin to allow highlight code in-line + +Also, when dealing with line highlighting on a enumerate list, kramdown will break the auto enumeration. +The only solution found by the team is to declare each list item as ` ` + +The ` ` basically blocks auto-numbering for numbered items. From 328b2d4f68db097e94bcfa26944868ed17284bd5 Mon Sep 17 00:00:00 2001 From: PerseoGI Date: Mon, 8 Jul 2024 12:02:45 +0200 Subject: [PATCH 4/6] Updated dates --- ...4-07-09-Introducing-vendoring-packages.markdown} | 6 +++--- .../standard-sdk-graph.png | Bin .../vendored-expanded-graph.png | Bin .../vendored-sdk-graph.png | Bin 4 files changed, 3 insertions(+), 3 deletions(-) rename _posts/{2024-06-24-Introducing-vendoring-packages.markdown => 2024-07-09-Introducing-vendoring-packages.markdown} (98%) rename assets/post_images/{2024-06-24 => 2024-07-09}/standard-sdk-graph.png (100%) rename assets/post_images/{2024-06-24 => 2024-07-09}/vendored-expanded-graph.png (100%) rename assets/post_images/{2024-06-24 => 2024-07-09}/vendored-sdk-graph.png (100%) diff --git a/_posts/2024-06-24-Introducing-vendoring-packages.markdown b/_posts/2024-07-09-Introducing-vendoring-packages.markdown similarity index 98% rename from _posts/2024-06-24-Introducing-vendoring-packages.markdown rename to _posts/2024-07-09-Introducing-vendoring-packages.markdown index c225241b..af49d245 100644 --- a/_posts/2024-06-24-Introducing-vendoring-packages.markdown +++ b/_posts/2024-07-09-Introducing-vendoring-packages.markdown @@ -232,11 +232,11 @@ Comparison between graphs:
- Standard sdk graph + Standard sdk graph Standard sdk graph
- Vendored sdk graph + Vendored sdk graph Vendored sdk graph
@@ -347,7 +347,7 @@ $ conan graph info . --build="sdk/1.0" -c tools.graph:vendor=build --format=html ```
- Vendored expanded graph + Vendored expanded graph Vendored expanded graph

diff --git a/assets/post_images/2024-06-24/standard-sdk-graph.png b/assets/post_images/2024-07-09/standard-sdk-graph.png similarity index 100% rename from assets/post_images/2024-06-24/standard-sdk-graph.png rename to assets/post_images/2024-07-09/standard-sdk-graph.png diff --git a/assets/post_images/2024-06-24/vendored-expanded-graph.png b/assets/post_images/2024-07-09/vendored-expanded-graph.png similarity index 100% rename from assets/post_images/2024-06-24/vendored-expanded-graph.png rename to assets/post_images/2024-07-09/vendored-expanded-graph.png diff --git a/assets/post_images/2024-06-24/vendored-sdk-graph.png b/assets/post_images/2024-07-09/vendored-sdk-graph.png similarity index 100% rename from assets/post_images/2024-06-24/vendored-sdk-graph.png rename to assets/post_images/2024-07-09/vendored-sdk-graph.png From f7eab91e848dd1390fc70a2f2df8aa0c9cd20cd3 Mon Sep 17 00:00:00 2001 From: PerseoGI Date: Mon, 8 Jul 2024 13:46:36 +0200 Subject: [PATCH 5/6] Fixed threads --- ...09-Introducing-vendoring-packages.markdown | 34 ++++++++----------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/_posts/2024-07-09-Introducing-vendoring-packages.markdown b/_posts/2024-07-09-Introducing-vendoring-packages.markdown index af49d245..58dfb4b1 100644 --- a/_posts/2024-07-09-Introducing-vendoring-packages.markdown +++ b/_posts/2024-07-09-Introducing-vendoring-packages.markdown @@ -2,8 +2,8 @@ layout: post comments: false title: "Introducing vendoring packages: Create and share packages decoupled from their dependencies" -meta_title: "(optional) A longer more descriptive title for search engines to index" -description: "Explore the capabilities of Conan's vendor feature and how can be used in a enterprise environment." +meta_title: "Introducing vendoring packages: Create and share packages decoupled from their dependencies" +description: "Explore the capabilities of Conan's vendoring feature and how can be used to decouple and vendor dependencies." keywords: "vendoring,package,repackaging,conan vendor,package_id,private" --- @@ -17,15 +17,16 @@ isolating implementation details across organization teams. Let’s dive into what "vendor" packages bring to the table and how they can benefit your workflow. -## What is a "vendored package"? +## What is a "vendoring package"? The "vendor" feature allows developers to distribute their software through Conan while keeping internal dependencies and recipes private. By enabling the vendor attribute in your Conan recipe, you can prevent Conan from downloading the recipes and binaries of your package's dependencies. This means you can -encapsulate all necessary binaries and libraries within your package, ensuring -that end-users have no access to your internal build details or private -repositories. +encapsulate all necessary binaries, libraries and other artifacts within your +package, ensuring that end-users have no access to your internal package +recipes, build details or private repositories. + ## Key Benefits @@ -43,7 +44,9 @@ repositories. Conan Center Index or a private artifact repository, pre-built binaries for various configurations can be included, ensuring that end-users receive a ready-to-use package without the need for additional downloads - - Vendoring can also be useful inside organizations by allowing sharing + - Vendoring can also be useful inside organizations by allowing sharing of + pre-compiled binaries that completely hide their dependencies, for + security, simplicity or convenience reasons. - SDKs between different work groups without sharing internals 3. **Reduced Build Times** @@ -52,9 +55,9 @@ repositories. dependency binaries or recipes from the server, potentially saving significant time and storage space, especially in production environments. -## Usage example +## Usage Example -For this example, make sure to at least have Conan v2.4.1 installed available. +For this example, ensure that you have at least Conan v2.4.1 installed.  1. Create a basic library from the CMake template @@ -107,13 +110,13 @@ $ conan graph info . --format=html > graph.html ```  6. Now let's dive into the vendoring feature. Some changes need to be made in the SDK ``conanfile.py``: -- As this example aims to make a vendored **static library**, we should first change the ``package_type`` accordingly +- As this example aims to make a vendoring **static library**, we should first change the ``package_type`` accordingly - Set the class attribute ``vendor`` to ``True``. This will enable the vendoring feature - Remove the unnecessary shared option from options and default_options, and remove configure method because it not needed anymore - Actually repackage the lib_a library inside the SDK. As we are generating a static library, we can achieve this by copying the ``liblib_a.a`` static library inside the SDK library - Finally, update the ``cpp_info.libs`` adding the ``lib_a`` dependency for consumers -```py{8 9 21 22 50 55} +```py{8 9 14 15 43 48} from conan import ConanFile from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps from conan.tools.files import copy @@ -125,13 +128,6 @@ class sdkRecipe(ConanFile): package_type = "static-library" vendor = True - # Optional metadata - license = "" - author = " " - url = "" - description = "" - topics = ("", "", "") - # Binary configuration settings = "os", "compiler", "build_type", "arch" options = {"fPIC": [True, False]} @@ -267,8 +263,6 @@ For releasing a vendored package, the creator should follow these steps: ## Advanced details -### Dependency graph and its importance - To understand the inner workings of the "vendor" feature, it is essential to grasp the concept of a dependency graph in Conan. When Conan builds the dependency graph for a package, it downloads the recipes and binaries for all From d129bdb422ca62db3558e2f35f12d3966eecaad5 Mon Sep 17 00:00:00 2001 From: PerseoGI Date: Tue, 9 Jul 2024 13:46:53 +0200 Subject: [PATCH 6/6] Support windows static libs on conanfile --- _posts/2024-07-09-Introducing-vendoring-packages.markdown | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_posts/2024-07-09-Introducing-vendoring-packages.markdown b/_posts/2024-07-09-Introducing-vendoring-packages.markdown index 58dfb4b1..79f7347f 100644 --- a/_posts/2024-07-09-Introducing-vendoring-packages.markdown +++ b/_posts/2024-07-09-Introducing-vendoring-packages.markdown @@ -158,8 +158,9 @@ class sdkRecipe(ConanFile): cmake.build() def package(self): - # Repackage static dependencies inside the package + # Repackage static dependencies inside the package supporting different OS copy(self, "*.a", src=self.dependencies["lib_a"].cpp_info.libdir, dst=os.path.join(self.package_folder, self.cpp_info.libdir)) + copy(self, "*.lib", src=self.dependencies["lib_a"].cpp_info.libdir, dst=os.path.join(self.package_folder, self.cpp_info.libdir)) cmake = CMake(self) cmake.install()