Skip to content

This project builds an Exiv2 Nuget package with static libraries and header files for the x64 platform and Debug/Release configurations.

Notifications You must be signed in to change notification settings

StoneStepsInc/exiv2-nuget

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

81 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Exiv2 Nuget Package

This project builds an Exiv2 Nuget package with static Exiv2 libraries and header files for the x64 platform.

Visit Exiv2 website for additional information about the Exiv2 project and library documentation:

https://github.com/Exiv2/exiv2

Package Configuration

The Exiv2 static libraries from this package will appear within the installation target project after the package is installed. The solution may need to be reloaded to make libraries visible. Both, debug and release libraries will be listed in the project, but only the one appropriate for the currently selected configuration will be included in the build. These libraries may be moved into solution folders after the installation (e.g. lib/Debug and lib/Release).

Note that the Exiv2 library path in this package will be selected as Debug or Release based on whether the selected configuration is designated as a development or as a release configuration via the standard Visual Studio property called UseDebugLibraries. Additional configurations copied from the standard ones will inherit this property.

Do not install this package if your projects use debug configurations without UseDebugLibraries. Note that CMake-generated Visual Studio projects will not emit this property before CMake v3.30.0.

CMake is configured to build Exiv2 with the following feature options:

  • BUILD_SHARED_LIBS=OFF
  • EXIV2_ENABLE_WEBREADY=OFF
  • EXIV2_ENABLE_CURL=OFF
  • EXIV2_ENABLE_BROTLI=OFF
  • EXIV2_ENABLE_INIH=OFF
  • EXIV2_ENABLE_VIDEO=ON
  • EXIV2_ENABLE_XMP=ON
  • EXIV2_ENABLE_SSH=OFF
  • EXIV2_BUILD_SAMPLES=OFF
  • EXIV2_BUILD_EXIV2_COMMAND=OFF
  • EXIV2_BUILD_UNIT_TESTS=OFF
  • EXIV2_BUILD_FUZZ_TESTS=OFF
  • EXIV2_ENABLE_PNG=ON
  • EXIV2_ENABLE_FILESYSTEM_ACCESS

Exiv2 Changes

Exiv2 source that was used to create this package contains a few changes applied in patches described in this section against the Exiv2 release indicated in the package version.

01-wide-char-paths.patch

Exiv2 dropped support for wide-character paths in v0.28.0 and did not provide any alternative ways to open file paths with characters outside of the currently selected Windows character set, such as Win-1252. Their intent appears to rely on Windows replacing various code pages with the UTF-8 code page, but support for this solution in Windows is limited at this point.

The Exiv2 source in this package is patched to restore limited file-only support for opening images with names comprised of valid Unicode characters.

Use methods that take std::filesystem:path to open images via wide-character path methods. The original methods should work as before, and will open file paths with characters in some code pages, but will not be able to open a file name like this - "hello-\U0001F30E.jpg".

Note that std::filesystem:path requires C++17 and will not compile in previous versions of C++.

See more details in the patch header and in the accompanying DGML file (requires DGML viewer, which may be installed in Visual Studio).

02-cmake-lists.patch

This patch adds a definition SUPPRESS_WARNINGS to disable Exiv2 warnings reported to one of the standard streams while reading EXIF.

All optimizations in debug builds have been disabled (Exiv2 enables /Ox and /Zo).

Exiv2 forces statically linked MSVC CRT (/MTd) for static library builds. This is changed to use dynamic MSVC CRT at all times to make sure memory managers are never mixed up in applications built against Exiv2 static libraries.

Exiv2 disables all forms of inlining by removing /Ob2 and /Ob1 from CMake-generated projects. This behavior is disabled because inlining provides good performance benefits and does not cause problems when includes and libraries are properly set up.

Release configurations have been changed to generate PDB files to aid debugging of released builds (e.g. crash dumps).

03-cmake-find-zlib-expat.patch

This patch disables CMake's find_package calls looking for zLib and Expat on the build machine, so these dependencies can be injected as Nuget packages during the build process.

Current version of Nuget does not provide support for updating .vcxproj files via command line tools, which limits CMake in being able to handle Nuget packages gracefully.

Migrating from 0.27.6

There are many breaking changes in Exiv2 v0.28.0, compared to v0.27.6. Some of the most notable ones are listed below.

  • Exiv2::Image::AutoPtr has been replaced with Exiv2::Image::UniquePtr.
  • The ubiquitous toLong() methods has been replaced with toInt64() for signed integers and with toUint32() for unsigned integers.
  • Exiv2::AnyError has been removed.
  • Exiv2::IfdId was changed to a scoped enum and will not compare against integer values.
  • Native support for wide-character paths has been removed. The patch 01-wide-char-paths.patch described above restores limited support for wide-character paths, but requires C++17.

Nuget dependencies patches

Nuget lacks the capability to install packages and update .vcxproj files via command line tools, which makes it impossible for CMake to integrate Nuget packages into Visual Studio projects generated by CMake. Patches with the pattern 2x-vs-nuget-*.patch are used as a work-around to inject Nuget dependencies into .vcxproj files.

These patches are extremely fragile and may not work when the next version of Nuget or CMake is installed on build machines or when Visual Studio changes the format of .vcxproj. If no alternative for supplying Nuget dependencies is found when patching stops working, this package will be updated to remove dependencies from Exiv2, which will disable XMP and PNG support.

The rest of this section describes how to create these patches, with the assumption that source code patches have already been applied.

Add goto :EOF in make-package.bat after CMake generates Visual Studio projects. Run this batch file from the project root to generate Visual Studio projects.

Copy the generated exiv2-0.28.0 directory as the base installation to run diff against later (e.g. exiv2-0.28.0_base).

Change to the Exiv2 source directory and run this command to start VS2022 with the generated exiv2.sln file.

devenv /Command View.PackageManagerConsole build\exiv2.sln

Run these commands to install dependencies into the first project.

Install-Package StoneSteps.zLib.VS2022.Static -ProjectName exiv2lib
Install-Package StoneSteps.Expat.VS2022.Static -ProjectName exiv2lib

There is another bug in Nuget that prevents installing packages into projects located in the same directory. In order to work around that bug, rename build\src\packages.config to packages.exiv2lib.config. Run this command to install the remaining dependencies.

Install-Package StoneSteps.zLib.VS2022.Static -ProjectName exiv2lib_int
Install-Package StoneSteps.Expat.VS2022.Static -ProjectName exiv2-xmp

For consistency, rename the newly-created packages.config to packages.exiv2lib_int.config. The one in the xmpsdk directory is created for a single project and can remain as package.config.

These commands modify .vcxproj files to add build instructions from installed packages, as well as include packages*.config files into the projects.

Open exiv2lib.vcxproj, exiv2lib_int.vcxproj and exiv2-xmp.vcxproj in a text editor and delete the entire line with package.config in each file that looks like this:

<None Include="packages.config" />

Without this step, the patch for exiv2-xmp.vcxproj picks up unrelated absolute file paths generated by CMake above it, which will cause conflicts when this patch is applied in another build environment.

If your CMake veersion is lower than 3.30.0, open build\exiv2.sln and change Use Debug Libraries in Advanced configuration settings for the Debug configuration of all three projects patched above. Note that Visual Studio project patches generated this way may only be applied in build environments with the CMake version that is lower than 3.30.0.

Generate patches for all three .vcxproj files in the same way using the base installation directory saved earlier.

devops\create-vs-nuget-patches exiv2-0.28.3_base exiv2-0.28.3

Note that .vcxproj files are expected to use CRLF line endings, but patch may get confused when it finds mixed line endings, which requires --binary option when patching. Because of that, make sure not to allow your IDE to correct CRLF line endings to LF when opening these patches in the editor, as patch instructions are expected to have LF line endings and chunks of .vcxproj files will have CRLF. This is expected.

Edit the generated patch file to make paths at the top to use forward slashes for path segment separators, remove quotes and add the top-level placeholder directories a and b, like this.

---- a/build/src/exiv2lib.vcxproj	2023-02-20 16:10:23.489175900 -0500
-+++ b/build/src/exiv2lib.vcxproj	2023-02-20 16:13:29.173423900 -0500
+--- "build_base\\src\\exiv2lib.vcxproj"	2023-02-25 13:04:41.665092600 -0500
++++ "build\\src\\exiv2lib.vcxproj"	2023-02-25 13:16:07.929154700 -0500

Inspect patches to make sure they don't contain absolute paths in referenced lines around the changes. If they do, these patches will not work on other build machines.

Building a Nuget Package

This project can build a Nuget package for Exiv2 either locally or via a GitHub workflow. In each case, following steps are taken.

  • Exiv2 source archive is downloaded from Exiv2's website and its SHA-256 signature is verified.

  • The source is patched to build in Visual C++ 2022.

  • CMake is used to generate Visual Studio project files.

  • Project files generated in the previous step are patched up to inject Nuget dependencies, as if they were configured in Visual Studio via Nuget functionality form.

  • VS2022 Community Edition is used to build Exiv2 libraries locally and Enterprise Edition to build libraries on GitHub.

  • Build artifacts for all platforms and configurations are collected in staging directories under nuget/build/native.

  • nuget.exe is used to package staged files with the first three version components used as a Exiv2 version and the last version component used as a package revision. See Package Version section for more details.

  • The Nuget package built on GitHub is uploaded to nuget.org. The package built locally is saved in the root project directory.

Package Version

Package Revision

Nuget packages lack package revision and in order to repackage the same upstream software version, such as Exiv2 v0.27.5, the 4th component of the Nuget version is used to track the Nuget package revision.

Nuget package revision is injected outside of the Nuget package configuration, during the package build process, and is not present in the package specification file.

Specifically, nuget.exe is invoked with -Version=0.27.5.123 to build a package with the revision 123.

Version Locations

Exiv2 version is located in a few places in this repository and needs to be changed in all of them for a new version of Exiv2.

  • nuget/StoneSteps.Exiv2.VS2022.Static.nuspec (version)
  • devops/make-package.bat (PKG_VER, PKG_REV, EXIV2_SHA256)
  • .github/workflows/build-nuget-package.yml (name, PKG_VER, PKG_REV, EXIV2_FNAME, EXIV2_DNAME, EXIV2_SHA256)

EXIV2_SHA256 ia a SHA-256 checksum of the Exiv2 package file and needs to be updated when a new version of Exiv2 is released.

In the GitHub workflow YAML, PKG_REV must be reset to 1 (one) every time Exiv2 version is changed. The workflow file must be renamed with the new version in the name. This is necessary because GitHub maintains build numbers per workflow file name.

For local builds package revision is supplied on the command line and should be specified as 1 (one) for a new version of Exiv2 and incremented with every package release for the same upstream version.

GitHub Build Number

Build number within the GitHub workflow YAML is maintained in an unconventional way because of the lack of build maturity management between GitHub and Nuget.

For example, using build management systems, such as Artifactory, every build would generate a Nuget package with the same version and package revision for the upcoming release and build numbers would be tracked within the build management system. A build that was successfully tested would be promoted to the production Nuget repository without generating a new build.

Without a build management system, the GitHub workflow in this repository uses the pre-release version as a surrogate build number for builds that do not publish packages to nuget.org, so these builds can be downloaded and tested before the final build is made and published to nuget.org. This approach is not recommended for robust production environments because even though the final published package is built from the exact same source, the build process may still potentially introduce some unknowns into the final package (e.g. build VM was updated).

Building Package Locally

You can build a Nuget package locally with make-package.bat located in devops. This script expects VS2022 Community Edition installed in the default location. If you have other edition of Visual Studio, edit the file to use the correct path to the vcvarsall.bat file.

Run make-package.bat from the repository root directory with a package revision as the first argument. There is no provision to manage build numbers from the command line and other tools should be used for this.

Sample Application

A Visual Studio project is included in this repository under sample-exiv2 to test the Nuget package built by this project. This application dumps all EXIF tags found in a supplied image file.

In order to build sample-exiv2.exe, open Nuget Package manager in the solution and install either the locally-built Nuget package or the one from nuget.org.

Binaries linking against this package need to include these Win32 libraries as linker input, similar to how this sample project does it. This is because of some functionality in Exiv2 that cannot be conditionally compiled, such as the Exiv2::http function.

  • psapi.lib
  • ws2_32.lib
  • shell32.lib

These library references are not automatically included within the package to keep having to link against network and Windows shell libraries visible.

About

This project builds an Exiv2 Nuget package with static libraries and header files for the x64 platform and Debug/Release configurations.

Topics

Resources

Stars

Watchers

Forks