(English) NuGet Packages for unstable, in-development Libraries – cont. (II)

Leider ist der Eintrag nur auf Amerikanisches Englisch verfügbar. Der Inhalt wird unten in einer verfügbaren Sprache angezeigt. Klicken Sie auf den Link, um die aktuelle Sprache zu ändern.

Previously, I wrote about using NuGet for software components, which are still in active development. One of the most important factors was the capability to override the nuget package’s content with content fetched from a directory, e.g., a working copy clone. The key element for this was a MSBuild variable NugetDevPackageTest_testLib_DevDir.

The original plan was to edit this variable using a project property page. While having a UI is nice, this has proven not to work on larger projects. The reason is simple: in larger projects, we talk about a vs solution with multiple vc projects, and many of these projects might reference our NuGet package. If we now need to switch to our local directory, we need to adjust the project properties for every project consuming the package. This is tiring and error prone. Forget to adjust just one project and you might end up with inconsistent builds. Therefore, I was seeking a more centralized configuration.

Dev. Override – II

The principle idea of having a variable to control the override remains valid. The targets in your nuget might look like this:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <!-- ... -->

  <!-- Compiler settings: defines and includes -->
  <ItemDefinitionGroup Condition="'$(NugetDevPackageTest_testLib_DevDir)' == ''">
    <ClCompile>
      <PreprocessorDefinitions>HAS_NUGETDEVPACKAGETEST_TESTLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
    </ClCompile>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(NugetDevPackageTest_testLib_DevDir)' != ''">
    <ClCompile>
      <PreprocessorDefinitions>HAS_NUGETDEVPACKAGETEST_TESTLIB;HAS_NUGETDEVPACKAGETEST_TESTLIB_DEVDIR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <AdditionalIncludeDirectories>$(NugetDevPackageTest_testLib_DevDir)\Project\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
    </ClCompile>
  </ItemDefinitionGroup>

  <!-- ... -->

</Project>

The obvious question is the definition of our DevDir variable.

For this is propose a central msbuild xml at the level of the vs solution!

We include it in our targets file, right after the root Project tag starts:

<ImportGroup>
  <Import Project="$(SolutionDir)packagesoverride.xml.user" Condition="Exists('$(SolutionDir)packagesoverride.xml.user')" />
</ImportGroup>

This line imports the msbuild xml file, if it exists. Notice, how the file name is generic and not related to our specific package. This is because multiple nuget packages can share this file!

The content of this central configuration is very simple:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <NugetDevPackageTest_testLib_DevDir>C:\Dev\SomeProject\Dir</NugetDevPackageTest_testLib_DevDir>
  </PropertyGroup>
</Project>

Now if you need to override a nuget package for your whole solution, just create this file!

Drawback 1: If the file did not exist previously, an you create it, then you need to rebuild all projects with nugets referencing this package. Because they might be affected by this file.

However, once the file does exist, changes to the file are currectly and automatically detected by visual studio, and build operations are correctly triggered in the affected projects. So, it might be a nice idea to keep a file with an empty property group in place, just in case.

If you need to override multiple nugets at once, just add multiple entries into this one property group.

Drawback 2: There is no UI. So you need to edit it in your favorite text editor, meaning, you are prone to all typing errors you can come up with.

All in all, I believe this central file for the nuget override configuration is an improvement.

Getagged mit: ,

Ich mag Shields.io

Ich bin ein visueller Kerl. Das heißt, ich mag alle möglichen Arten von visuellen Repräsentationen abstrakter Daten. Natürlich mag ich also auch die ganzen fetzigen Badges die überall im Internet rumfahren. Es macht also nur Sinn dass ich die auch selber nutze.

Shields.io stellt Badges für Source-Code-Zeug zur verfügung. Und so, ohne Umschweife, hier ist die visuelle Zusammenfassung aller Nuget-Pakete die ich betreue:

Bei Altem bleiben?

Es ist Zeit einen meiner Lieblingsstrips von xkcd zu rebloggen: tar

I don’t know what’s worse–the fact that after 15 years of using tar I still can’t keep the flags straight, or that after 15 years of technological advancement I’m still mucking with tar flags that were 15 years old when I started.

Es gibt viele solche Tools die noch heute benutzt werden, obwohl sie von Anfang an Müll waren. Jaja, das Beispiel hier macht sich über Nix lustig. Es gibt aber auch viele ähnliche Beispiele aus dem MS-Windows-Ökosystem.

Ich mach mich gar nicht über diese Tools lustig. Ich mach mich über die Leute lustig die noch heute diese Tools befürworten!

Getagged mit: ,

Serverumzug

Ich habe meinen alten Webhoster hinter mir gelassen und meine Webseite umgezogen.

Das war jetzt dringend nötig.

Ich hab auch das WordPress aktualisiert. Nun scheint das Plugin für mehrsprachige Posts nicht mehr richtig mit dem Editor zu funktionieren. Naja. Das ist ein Problem für ein anderes Mal.

SpringerJagd ist beendet

Meine Spielidee „SpringerJagd“ werde ich nicht weiter verfolgen.

Das Projekt ist schlicht der notwendigen Priorisierung meiner ganzen Projekte zum Opfer gefallen. Schade, aber es ist besser so. So habe ich mehr Zeit gezielt für andere Projekte. Und von denen werde ich schon bald hier schreiben. Und mit „bald“ meine ich in ein paar Monaten, oder so.

(English) Beautify HtmlAgilityPack

Leider ist der Eintrag nur auf Amerikanisches Englisch verfügbar. Der Inhalt wird unten in einer verfügbaren Sprache angezeigt. Klicken Sie auf den Link, um die aktuelle Sprache zu ändern.

HtmlAgilityPackAs can be read on the internet: HtmlAgilityPack is not for beautiful, aka human readable, html files.

“[…] it’s a ‘by design’ choice.” [https://stackoverflow.com/a/5969074]

So everyone redirects you to some other library.

Now, I am a bit stubborn. I want to use HtmlAgilityPack and I want to have indented, human-readable html files. The magic is within text nodes in the DOM. So, I wrote two utility functions to help me out.

First, to get rid of all unwanted whitespaces. This one might be a bit aggressiv, but it was ok for me:

static private void removeWhitespace(HtmlNode node) {
  foreach (HtmlNode n in node.ChildNodes.ToArray()) {
    if (n.NodeType == HtmlNodeType.Text) {
      if (string.IsNullOrWhiteSpace(n.InnerHtml)) {
        node.RemoveChild(n);
      }
    } else removeWhitespace(n);
  }
}

And, second, to create white spaces for line breaks and indentions:

internal static void beautify(HtmlDocument doc) {
  foreach (var topNode in doc.DocumentNode.ChildNodes.ToArray()) {
    switch (topNode.NodeType) {
      case HtmlNodeType.Comment: {
          HtmlCommentNode cn = (HtmlCommentNode)topNode;
          if (string.IsNullOrEmpty(cn.Comment)) continue;
          if (!cn.Comment.EndsWith("\n")) cn.Comment += "\n";
        } break;
      case HtmlNodeType.Element: {
          beautify(topNode, 0);
          topNode.AppendChild(doc.CreateTextNode("\n"));
          //doc.DocumentNode.InsertAfter(doc.CreateTextNode("\n"), topNode);
        } break;
      case HtmlNodeType.Text:
        break;
      default:
        break;
    }
  }
}

private static bool beautify(HtmlNode node, int level) {
  if (!node.HasChildNodes) return false;

  var children = node.ChildNodes.ToArray();
  bool onlyText = true;
  foreach (var c in children) {
    if (c.NodeType != HtmlNodeType.Text) onlyText = false;
  }
  if (onlyText) return false;

  string nli = "\n" + new string('\t', level);

  foreach (var c in children) {
    node.InsertBefore(node.OwnerDocument.CreateTextNode(nli), c);
    if (c.NodeType == HtmlNodeType.Element) {
      if (c.HasChildNodes) {
        if (beautify(c, level + 1)) {
          c.AppendChild(c.OwnerDocument.CreateTextNode(nli));
        }
      }
    }
  }
  return true;
}

As you might see, the code is pretty hacky. But, it works for me. Maybe, it also works for you, or it can be a starting point.

Getagged mit: ,

Another Nuget: TeamCity GoogleTest Streamer

Und hier ist nochmal ein neues Nuget Package:

https://bitbucket.org/sgrottel_nuget/teamcity-gtest-streamer

https://www.nuget.org/packages/teamcity-gtest-streamer

Eine kleine Bibliothek für eine bessere Integration von GoogleTest in TeamCity. Tatsächlich ist es eine einzelne Datei als Header Only Library. Es gibt trotzdem ein Nuget-Paket für einfache Integration und Updates.

Getagged mit: ,

GLM 0.9.9 NuGet

GLM ist eine nette, schlanke, gute mathe Bibliothek für OpenGL. Sie ist header-only. Macht ein NuGet trotzdem sinn? Ich meine „Ja“. Die einfache Benutzung und die saubere Versionierung sind den minimalen Mehraufwand absolut wert.

Ich habe nun auch die Wartung des GLM Nuget-Pakets mit übernommen und das Paket auf die Version 0.9.9 aktualisiert.

Getagged mit: ,

Lua Nuget aktualisiert auf 5.3.5

Lua wurde auf 5.3.5 aktualisiert.

Also habe ich auch mein NuGet-Paket auf Lua 5.3.5 aktualisiert.

Getagged mit: ,

(English) NuGet Packages for unstable, in-development Libraries

Leider ist der Eintrag nur auf Amerikanisches Englisch verfügbar. Der Inhalt wird unten in einer verfügbaren Sprache angezeigt. Klicken Sie auf den Link, um die aktuelle Sprache zu ändern.

This article discusses a way how to create and use a NuGet Package for Libraries which are not yet stable and which are still under active development.

Especially, the core idea is to develop such an library and a project consuming this library in an efficient way.

Concept

As we develop on Windows with Visual Studio, NuGet is a powerful tool to manage library dependencies in Projects, for external 3rd Party libraries, as well as for our own libraries. Using Git our alternatives would be:

  • Git Submodules – with their known problems (detached head, multiple commits, etc.)
  • cmake – vs. Visual Studio solutions
  • manual paths / zip packages – manual work

Using NuGet seems the way to go for Windows-Only projects. Even for native c++ components. (Side note: coapp is dead and is not missed.)

The Problem

However: if we have a library component and a consuming project, which we have to actively develop both at the same time, the NuGet process gets prohibitively tiring. For each change in the library, we need to build the lib, repack the NuGet package, increasing its (build) version number, update the consuming project’s NuGet reference, and build the project… For every tiny typo.

The Idea

The core idea is to prepare the NuGet package in such a way, that a local override of the package’s content is possible. Think of an environment variable holding the file system path to the library’s development directory. If this environment variable is not set, the content of the NuGet package is use. If the environment variable is set, then include paths and library paths are set to directly use the files found in that development directory (obviously, resulting in an unclean in-development version of the project).

Since native NuGet packages simply use an MSBuild script to integrate themselves into a project, we have (almost) all the possibilities MSBuild has to offer.

Set up the NuGet Package

Creating a native C++ library NuGet package without coapp is not very difficult, but out-of-focus of this article. For the sake of simplicity I only discuss the (second) easiest task: a static library. We need some header file and some static library files. All in all the whole file looks like this:

NugetDevPackageTest.testLib.nuspec

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
  <!--
    Create package:
    cd solution directory
    nuget pack NugetDevPackageTest.testLib.nuspec -Version 0.2.0-build001
    -->

  <!-- package metadata -->
  <metadata>
    <id>NugetDevPackageTest.testLib</id>
    <version>$version$</version>
    <title>NugetDevPackageTest testLib</title>
    <authors>Sebastian Grottel</authors>
    <projectUrl>https://bitbucket.org/sgr_faro/nuget-dev-package-test/overview</projectUrl>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>Nuget package of `someLib` to test dev nuget package concept</description>
    <tags>nugetdevpackagetest testLib native</tags>
  </metadata>

  <!-- content -->
  <files>
    <!-- include header files -->
    <file src="DemoClass.h" target="build\native\include" />

    <!-- static libraries -->
    <file src="lib\Win32\Debug\some.lib" target="build\native\lib\Win32\Debug" />
    <file src="lib\Win32\Release\some.lib" target="build\native\lib\Win32\Release" />
    <file src="lib\x64\Debug\some.lib" target="build\native\lib\x64\Debug" />
    <file src="lib\x64\Release\some.lib" target="build\native\lib\x64\Release" />

    <!-- build rules -->
    <file src="NugetDevPackageTest.testLib.targets" target="build\native" />

  </files>
</package>

There are two interesting aspects to highlight here:

  1. The static libraries can be collected from any directories. Important is that you specify the correct target directories. Follow the semantic rules NuGet specifies.
  2. The first build rule file, NugetDevPackageTest.testLib.targets, controls the integration of the NuGet Package’s content into the Visual Studio project. This file must have the same name as the package itself.

We set up the targets file as follows:

NugetDevPackageTest.testLib.targets

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <!-- If development directory is empty, use content from the package -->
  <PropertyGroup Condition="'$(NugetDevPackageTest_testLib_DevDir)' == ''">
    <__NugetDevPackageTest_testLib_IncludeDir>$(MSBuildThisFileDirectory)include/</__NugetDevPackageTest_testLib_IncludeDir>
    <__NugetDevPackageTest_testLib_LibrarayPath>$(MSBuildThisFileDirectory)lib\$(Platform)\$(Configuration)\some.lib</__NugetDevPackageTest_testLib_LibrarayPath>
  </PropertyGroup>

  <!-- If development directory is set, use the content found there -->
  <PropertyGroup Condition="'$(NugetDevPackageTest_testLib_DevDir)' != ''">
    <__NugetDevPackageTest_testLib_IncludeDir>$(NugetDevPackageTest_testLib_DevDir)/</__NugetDevPackageTest_testLib_IncludeDir>
    <__NugetDevPackageTest_testLib_LibrarayPath>$(NugetDevPackageTest_testLib_DevDir)\lib\$(Platform)\$(Configuration)\some.lib</__NugetDevPackageTest_testLib_LibrarayPath>
  </PropertyGroup>

  <!-- If development directory is set, define an additional macro -->
  <ItemDefinitionGroup Condition="'$(NugetDevPackageTest_testLib_DevDir)' != ''">
    <ClCompile>
      <PreprocessorDefinitions>HAS_NUGETDEVPACKAGETEST_TESTLIB_DEVDIR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ClCompile>
  </ItemDefinitionGroup>

  <!-- Compiler options: package macro and include paths -->
  <ItemDefinitionGroup>
    <ClCompile>
      <PreprocessorDefinitions>HAS_NUGETDEVPACKAGETEST_TESTLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <AdditionalIncludeDirectories>$(__NugetDevPackageTest_testLib_IncludeDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
    </ClCompile>
    <ResourceCompile>
      <AdditionalIncludeDirectories>$(__NugetDevPackageTest_testLib_IncludeDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
    </ResourceCompile>
  </ItemDefinitionGroup>

  <!-- Linker options: library (full) path -->
  <ItemDefinitionGroup>
    <Link>
      <AdditionalDependencies>$(__NugetDevPackageTest_testLib_LibrarayPath);%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>

</Project>

Create the NuGet package:

nuget pack NugetDevPackageTest.testLib.nuspec -Version 0.2.0-build002

(obviously, you adjust the version number as you like to.) and be happy that it already does work as you intended: if the environment variable NugetDevPackageTest_testLib_DevDir is set, the content of this folder is used instead of the NuGet package’s content.

Setting an environment variable and the need to restart Visual Studio any time you change it, this is obviously not great. We can improve on that by adding a GUI.

Visual Studio Property Page

We add an Visual Studio property page for your project to the NuGet package, allowing us to change the setting of the variable from within Visual Studio. For this we add an XML description of the values we want to change:

overrideSettings.xml

<?xml version="1.0" encoding="utf-8"?>
<ProjectSchemaDefinitions xmlns="clr-namespace:Microsoft.Build.Framework.XamlTypes;assembly=Microsoft.Build.Framework">

  <!-- Project property settings to override content of nuget development packages -->
  <Rule Name="ProjectSettings_NugetDevOverride" PageTemplate="tool" DisplayName="Nuget Development Override" SwitchPrefix="/" Order="1">

    <!-- Category to identify this package -->
    <Rule.Categories>
      <Category Name="NugetDevPackageTest_testLib" DisplayName="NugetDevPackageTest.testLib" />
    </Rule.Categories>

    <!-- Store override settings only in the user file -->
    <Rule.DataSource>
      <DataSource Persistence="UserFile" ItemType="" />
    </Rule.DataSource>

    <!-- Override development path -->
    <StringProperty Name="NugetDevPackageTest_testLib_DevDir"
      DisplayName="Development Directory"
      Subtype="folder"
      Description="Override content of Nuget package with content of this directory. If this field is empty (default) the content of the nuget package is used. This value is persistent as user setting only."
      Category="NugetDevPackageTest_testLib" />

  </Rule>
</ProjectSchemaDefinitions>

Note that the StringProperty directly stores it’s value in the variable we are already using. Make sure not to use this variable as an Environment variable anymore!

Now, we reference this property page from the other files of our NuGet package:

NugetDevPackageTest.testLib.nuspec

...
    <!-- build rules -->
    <file src="NugetDevPackageTest.testLib.targets" target="build\native" />
    <file src="overrideSettings.xml" target="build\native" />

  </files>
</package>

 

NugetDevPackageTest.testLib.targets

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <!-- include the gui property page to override the development directory -->
  <ItemGroup>
    <PropertyPageSchema Include="$(MSBuildThisFileDirectory)\overrideSettings.xml" />
  </ItemGroup>

  <!-- If development directory is empty, use content from the package -->
...

Now create the new version of the package. Update the package reference in your consuming project. You might need to restart visual studio once. And you should find the new property page in the settings of your project.

Use the NuGet Package or the local Version

Be Aware of all the pitfalls

  • Never commit the .user file of your projects to the repository!
  • If your project code requires local changes within the code usually packed into NuGet, your project will not compile correctly on any other machine!
    • Before you push, or at least before you create a pull request, you need to push and publish the changed NuGet project and you need to reference the NuGet package cleanly without override.
  • You need to use two Visual Studios running, one for your project and one for the NuGet-ted project.
    • If you run your project in the debugger, and you step into functions from the NuGet project, the corresponding source file is opened in the Visual Studio of your project. You can edit the files there, but of course you cannot compile the NuGet library here. Remember to switch to the right Visual Studio for that.

The Proposed Process

testApp – Visual Studio #1 testLib – Visual Studio #2 Comment
Clone testApp
Open testAppsolution in Visual Studio #1
Build testApp This fetches the NuGet package of testLib and builds the App with the content of that package, e.g. version 1.0.0
Clone testLib
Open testLib solution in Visual Studio #2
Build testLib
(Build testApp) Nothing changes. Still the NuGet package is used. Build should not do anything (up to date)
Open Project settings and override the nuget settings. Reference the directory where you cloned and built testLib
(Build testApp) Known Issue: Visual Studio still believes the project would be up to date
Rebuild testApp App is now built with the testLib built in your local directory!
(Build testApp) The project is up to date (which is correct)
Change some code and build testLib
Build testApp The project correctly detects that it needs to build (at least to link) again. The changes of the local testLib can directly be used.
You start testApp in the Debugger You can even enter code of testLib, look at local / private variables, etc. The corresponding source files of testLib are opened in Visual Studio #1!
You edit source of testLib WRONG Visual Studio!
You hit Build → testApp is up to date It is the wrong Visual Studio. You cannot build testLib there. You need to switch to the other Visual Studio
Visual Studio might detect the changes at the source files, if they are opened. If the files are not opened, everything is ok. If Visual Studio does ask you, select to keep changes made outside Visual Studio #2
Hit F5 (start Debugging) Binary and source code do not match! Visual Studio #1 should warn you about it if you try, e.g., to set a breakpoint in source code of testLib
Build testLib The testLib is update with the changes to the source code you made in Visual Studio #1
Hit F5 (start Debugging) Visual Studio #1 correctly detects the updates of testLib, builds again, and executes testApp with the newest version of testLib.
 

 

 

Develop your changes in testApp and testLib. Now you are finished and want to push a stable version to your CI system

Be sure testLib is correct in all configurations
Commit + Push testLib
Trigger built / update of new NuGet package (we call it Ver++)
Disable the override settings for the NuGet package
Update the testApp project to use the NuGet package Ver++ Ideally this package should be the result of your CI system.
Compile and testApp in all configurations Since you had the identical code tested before locally, this should not show any problems. If it does, you likely missed something before.
Commit + Push your final update of testApp This should be solely the increment of the NuGet package version
Build / Test your testApp on your CI system All should work fine.

Known Issues

Visual Studio Property Pages GUI

The Visual Studio GUI is sometimes not correctly updated. One such situation is created by the property page descriptions injected via NuGet packages.

If you initially add a NuGet package with such a property page, or if the NuGet package you are using changed its property page, Visual Studio might not show you the right proptery page (or might not show any property page).

If this happens, restart Visual Studio after installing / restoring the NuGet package and you should be fine.

Visual Studio’s Fast Up to Date Check misses Changes of the StringProperty

When in Visual Studio a build is triggered (e.g. by pressing F5 for a Debug start), Visual Studio does not invoke MSBuild to determine if the project is up to date. Instead is uses an internal mechanism to quickly decide if a rebuild is required. This internal mechanism, known as fast up to date check, does not perform all required checks. E.g., it misses changed to the paths defined by the variable defined within the property page GUI created by our NuGet package.

This means, whenever the value of this variable is changed, you should trigger a rebuild of the consuming project.

One workaround would be to disable the fast up to date check for this project. However, for large projects, like SCENE, this introduces more pain than the required rebuild.

cf.: https://stackoverflow.com/q/47516374/552373

Version Schizophrenia

There is an semantic problem: you reference a Nuget package version, e.g. 1.0.0. But you override the content with changed files. Those will eventually be published as a new version, e.g. 1.0.1. But until then, your consuming project will reference those as version 1.0.0.

Your build process must be aware of this issue.

You should introduce integration tests to make sure such information mismatch never enters release branches.

Deep Dependency Graph Version Conflict

Consuming the unstable library from the development directory, is a project setting, not a solution setting. If you have a deep dependency graph and you are consuming this library via multiple routes, the override setting only influences one single route! Especially, if you use another Nuget package which uses your lib nuget, you cannot do anything about it. You need to be carefully aware of potential binary compatibility issues in such a construct.

Workarounds could be based compile-time and run-time checks of version numbers and the „dirty-lib-flag“ (cf. HAS_NUGETDEVPACKAGETEST_TESTLIB_DEVDIR).

Top