Skip to content

Packaging

Brian Bowman edited this page Mar 21, 2017 · 40 revisions

Packaging

This document discusses the way WinObjC is packaged and released to customers along with tips for contributors building and consuming their own packages.

What is a Package?

WinObjC is shipping its code and build integration via NuGet. NuGet packages are .zip files that carry semantic versioning information and conventions for discovery and installation. More information about what a package is and the details of creating and consuming a NuGet package can be found here.

How does a developer get / update / use a WinObjC package?

NuGet operates on the notion of feeds of packages where a package producer (the WinObjC team) uploads and hosts its packages. For WinObjC, we are choosing to host our packages on nuget.org, the default package feed location. Both Visual Studio's package manager and the NuGet commandline client (which can be downloaded here) have this feed enabled by default. To install a WinObjC package, a developer simply needs to declare a dependency (via either project.json or PackageReference) to one of our packages (see below), and run nuget restore from the commandline or simply build the solution in Visual Studio. Updating is as simple as declaring a different version dependency. See the NuGet documentation for an in depth overview.

What Packages Does WinObjC Produce?

winobjc-tools

Command line tools / entry point into WinObjC. Includes vsimporter and Visual Studio Extensions

WinObjC.Language

Objective-C language support. Includes the compiler (clang) and the runtime (libobjc2)

WinObjC.Logging

Common logging package shared between Language and Frameworks

WinObjC.Frameworks.Core

Core Objective-C Frameworks (Foundation and things with Core in the name). Factored out to share between Frameworks UWP and Frameworks

WinObjC.Frameworks.UWP

Universal Windows Platform Frameworks like Cortana

WinObjC.Frameworks

All of the remaining Objective-C default Frameworks

WinObjC.Frameworks.ThirdParty

Third Party Dependencies that WinObjC relies on

WinObjC.Packaging

Meta package that aids in creation of other packages. Especially useful for Middleware.

How are WinObjC Packages Versioned?

WinObjC uses GitVersion with a few customizations (great documentation on all these options is found here) to automatically determine package versions based on the git repository tags and branching structure. The WinObjC release process follows a very similar workflow to GitFlow and thus the ground truth for our releases comes from the master branch tags. These tags are created during the release process to snapshot the state of the repository at that time. Our nightly builds and local development then start the cycle for the next release and thus get an incremented patch number along with a prerelease string indicating when they were created.

Example

WinObjC.Frameworks.0.2.170229-pr-20170318003711
\----------------/\---------/\----------------/
        |              |            |
       (1)            (2)          (3)
  1. Package Name
  2. Latest Git Tag + 1 - Master builds don't get the + 1
  3. Pre-release string with timestamp - Master builds aren't pre-release

FAQ for daring developers, cunning contributors, and postulating porcupines

Which Packages do I need in my project?

Add WinObjC.Frameworks and WinObjC.Language to your project. These are the "leaf nodes" and will cause most of the other packages to be included through their dependency chain.

If you are packaging your own code into a NuGet (for middleware as an example), please add WinObjC.Packaging to simplify the process.

How do I upgrade to Packaging from an old SDK?

If you are using a prebuilt SDK, download Visual Studio 2017, and re import your app using the latest winobjc-tools command line package (Instructions on README).

If you are building the SDK locally, please install Visual Studio 2017, the Nugetizer VSIX (either from the winobjc-tools package or directly as described below), and make sure any local/internal NuGet feeds are configured to pick up latest beta packages if desired. Please see the below for how to consume a new locally built SDK.

How are WinObjC packages built locally?

Simply build build.sln or tools.sln to produce the packages. WinObjC is leveraging Nugetizer in order to integrate package creation into the build. These are the .nuproj projects that are now in the solution. This project works by leveraging MSBuild to walk project references, infer project output and ultimately create the packaged content.

How do I add something to a WinObjC package?

Project output is automatically inferred from all projects that the .nuproj references. As long as the project mimics the other projects in the WinObjC repo, it should automatically add its contents to the package. If a project has "Extra" content that it needs to stick in the package, a custom MSBuild target that runs before GetPackageContents that adds PackageFile items can be used. See CoreFoundation.vcxproj's AddExtraPackageItems target as an example.

How do I consume packages locally?

NuGet allows using a local directory as a package feed when restoring packages. In order to make this as seamless as possible, the output directories for WinObjC packages have been added to the nuget.config files where feasible (in the samples directory for instance). In cross repository scenarios, please set WINOBJC_SDK_ROOT to your cloned directory and configure the nuget.config to use that.

In addition to finding packages, consuming a just produced package locally means updating the consumer to automatically pick up the new version. Luckily by adding winobjc.packagereference.override.targets as an import to your project (an example can be found at bottom of WOCCatalog.vcxproj) the version that the consumer depends on can be swapped out on the fly. Again for cross repo scenarios, it advisable to set WINOBJC_SDK_ROOT.

NOTE: Local packages are always preferred when using the override targets so make sure to not forget about locally produced packages on the machine that may be influencing builds.

Extremely Advanced and Extremely Detailed Example

When discussing how to find and use local packages it very useful to separate the package producer persona from that of the consumer. In the below example I will discuss a hypothetical scenario in which Alice and Bob work on an app together that uses WinObjC and, as advanced users, they wish to make local changes to WinObjC and share those changes with each other.

  1. Alice clones WinObjC and makes changes to fix a small bug in UIKit. She sets an environment variable called WINOBJC_SDK_ROOT to where she cloned the repo so that her awesome app can easily point back to it.

  2. Alice builds build.sln and sees new output packages at:

\build\OutputPackages\Debug

and

\build\OutputPackages\Release

(If Alice builds a change for the language or logging packages, these packages will be in \tools\OutputPackages\Release and \tools\OutputPackages\Debug)

  1. To try out her changes locally, Alice adds the following lines to the nuget.config next to her awesome app's visual studio solution (if she does not have she should consider adding one to control how nuget is configured for that solution):
  <packageSources>
    <add key="Local WinObjC Tools Packages" value="%WINOBJC_SDK_ROOT%\tools\OutputPackages\Release\" />
    <add key="Local WinObjC Tools Packages - DEBUG" value="%WINOBJC_SDK_ROOT%\tools\OutputPackages\Debug\" />
    <add key="Local WinObjC Frameworks Packages" value="%WINOBJC_SDK_ROOT%\build\OutputPackages\Release\" />
    <add key="Local WinObjC Frameworks Packages - DEBUG" value="%WINOBJC_SDK_ROOT%\build\OutputPackages\Debug\" />
  </packageSources>

The full nuget.config would look like:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="Local WinObjC Tools Packages" value="%WINOBJC_SDK_ROOT%\tools\OutputPackages\Release\" />
    <add key="Local WinObjC Tools Packages - DEBUG" value="%WINOBJC_SDK_ROOT%\tools\OutputPackages\Debug\" />
    <add key="Local WinObjC Frameworks Packages" value="%WINOBJC_SDK_ROOT%\build\OutputPackages\Release\" />
    <add key="Local WinObjC Frameworks Packages - DEBUG" value="%WINOBJC_SDK_ROOT%\build\OutputPackages\Debug\" />
  </packageSources>
  <activePackageSource>
    <add key="All" value="(Aggregate source)" />
  </activePackageSource>
  <disabledPackageSources />
</configuration>

This way her app can locate the local packages based on the WINOBJC_SDK_ROOT variable. See here for more info on nuget.config

  1. Now that her app can find the locally built packages, Alice needs to update the awesome app to look for the new version of the packages. If she is using project.json, she changes the version of her dependencies to match what was just built. For instance she might change:
. . . 
  "dependencies": {
    "WinObjC.Frameowrks": "*"
  },
. . .

to be:

. . . 
  "dependencies": {
    "WinObjC.Frameowrks": "0.2.170229-dev-20170318003711"
  },
. . .

(the actual version should match what is in Alice's OutputPackages folder). A more in depth discussion of NuGet Version specifiers is here

If she is using <PackageReference> she can instead choose to add the following to her app's .vcxproj:

  <Import Project="$(WINOBJC_SDK_ROOT)\common\winobjc.packagereference.override.targets" Condition="Exists('$(WINOBJC_SDK_ROOT)\common\winobjc.packagereference.override.targets')"/>  

This allows her project to dynamically pick up the just built versions from the output directory to make it a little easier to consume new packages.

  1. Bob has not cloned WinObjC but wants to try out Alice's awesome changes.

  2. Alice puts her packages on a file share, internal NuGet feed, or otherwise shares them with Bob.

  3. Bob updates his nuget.config and project.json to look at these packages and tries out the changes.

  4. When Alice and Bob are happy with the changes they submit a pull request so that the change can make its way to the packages on the official nuget feed.

NOTE: The above steps are already done for the sample apps in the WinObjC repo and the hope is that most consumers will be able to just use the official package feed without producing new packages themselves.

How do I publish a new package?

The short answer is that you don't. Packages are only built locally to allow a contributor or advanced developer to test out changes. Official packages hosted on nuget.org are built by our build machines and must go through release validation. If you require changes to a package in order to successfully use it, please submit a pull request so that other WinObjC consumers can benefit as well.

What does Any CPU mean for native code?

The Any CPU build platform is used to indicate that both ARM and x86 builds should occur. This allows a single build to produce a full package (which is what our build machine uses). To speed up local development, build x86 or ARM will build a package that only works for that single platform.

NOTE: If you switch platforms, make sure to build a new package for that platform for any local packages previously made.

Where are packages installed?

Packages are installed to the nuget cache. This is typically located at C:\Users\your_username\.nuget The Visual Studio Package Manager has a Clear Cache button that can be used if you need to free up space.

Known Issues

2160 Clang Windows pop up all over the place.

Cause not known but closing and opening visual studio / restarting the machine seems to make it go away. Several devs hit this briefly but then did not encounter it again.

4307 Visual Studio Nuget Restore fails with

Error occurred while restoring NuGet packages: The operation failed as details for project SomeProjectName could not be loaded

Try turning on Lightweight Solution Load (it'll make loading our large build.sln solution faster anyway). This is an untested suggestion from the NuGet folks so your mileage may vary.

4633 Despite needing to restore, Visual Studio NuGet Restore shows:

Output window for Package Manager:
All packages are already installed and there is nothing to restore.
Time Elapsed: 00:00:00.5279550
========== Finished ==========

Try restoring from the commandline. Run init.ps1 from the repo root to get a copy of nuget in your .tools directory and then restore with:

.tools\nuget.exe restore .\build\build.sln

[N/A] Builds fail with Accelerate.lib is missing, can't find ErrorHandling.h or other errors that seem like packages are missing.

Make sure that packages are restored (Run .tools\nuget.exe restore .\build\build.sln from the command line). Visual Studio 2017 has an issue where it sometimes claims projects are up to date even if they are not. see 4633 below for more information.

[N/A] Visual Studio can't open build.sln / tools.sln because it can't recognize .nuproj

Make sure that you have winobjc-tools installed (see instructions on main README) or manually install the NuGetizer VSIX.

[N/A] NuGet Restore from the commandline doesn't seem to do anything.

Make sure that you have selected MSBuild as an install step in VS 2017. The new PackageReference style of dependencies only work in MSBuild version 15 or greater. The first line of nuget restore will tell you which version of MSBuild is being used.

Several of our devs accidentally had MSBuild 14.0 in their PATH variable that was overriding the one nuget used.

Note that you can override the msbuild version used with:

-msbuildversion 15

more information

[N/A] After pulling latest develop (with packaging) I can't seem to debug anymore. Where did my symbols go?

The symbols for each release should still be indexed nightly. Please file issues for anything the symbol server isn't finding for you.

Local symbol files have moved according to where they are packaged and can be found in either the build\ or tools\ sub directories in similar spots to where they were before (\deps\prebuilt\Universal Windows for prebuilt binaries and Win32\Release\Universal Windows for built frameworks). Please adjust your symbol search paths accordingly.

[N/A] My project isn't using the latest package from nuget.org. What gives?

Make sure that you correctly have the version you want to use specified in your project.json, as a PackageReference, or in your packages.config. If you are building a sample app or the SDK itself, make sure you don't have an old locally built package preventing you from using the version from the feed.

Re-run package restore and you should have the desired package now installed.

2237 Build break when building "Any CPU" configuration

You can build individual CPUs instead. (x86 or ARM).