Skip to content

Packaging

Brian Bowman edited this page Apr 12, 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.

What benefit does a developer get from WinObjC shipping via packages?

  1. Updateability

    NuGet packages are versioned and hosted on nuget.org. This makes it really easy for a developer to specify what version of a package to use via the NuGet Package Manager or the project.json file. When a new version of WinObjC ships, that developer can choose to automatically get the latest version by using a floating version specification ("*") or a developer can grab a specific version that works for his or her scenarios. Updating is as simple as changing the version that a developer depends on.

    A developer can also choose to try out our daily pre-release builds if some required functionality is fresh off the presses. This additional version control is seamlessly integrated into the project and doesn't require managing multiple build drops and wiring up projects to different local folders etc.

  2. Portability

    Projects using WinObjC shouldn't need to rely on the state of the machines they are being built on as much as possible. By using NuGet pacakges, projects using WinObjC can be shared / built on different machines with much less configuration / management between machines. As long as Visual Studio 2017 or later is installed (if only this could be described as a dependency as well), a project should be able to be built.

  3. Lightweight

    By not shipping WinObjC as a monolithic zip file, a developer that wants to get started doesn't need to download or build everything to get going. A one line command to install the winobjc-tools package lets a developer quickly see if an application can be imported and then the project handles all dependencies itself in a self contained manner.

  4. Componentization

    WinObjC is a large project that tries to provide functionality for the many frameworks that Objective C developers expect to be available to them. Naturally, a project may not need every single framework and in some cases, a developer may even wish to provide a custom version of certain frameworks. By componentizing WinObjC, a developer can pick and choose what to use more easily (for instance an app written against CoreFoundation may not need anything beyond the Frameworks.Core package). Additionally, this componentization has allowed the WinObjC team to directly use the same compiler and build tools as a developer would increasing coverage and robustness.

  5. Flexibility

    WinObjC is primarily directed at existing Objective-C applications, however, there is nothing stopping a developer adding Objective-C code to an existing project. Using NuGet packages makes this process easier and more familiar (just need to add the dependencies). This allows WinObjC to be used in a wider range of scenarios for our developers.

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?

Package Details (Click to Expand)

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, follow the advanced installation instructions, 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 (for example in your app), please set the WINOBJC_SDK_ROOT environment variable to your cloned directory and configure the nuget.config to use that as described in the below example.

In addition to finding packages, consuming a just produced package locally means updating the consumer to pick up the new version. How this is done depends on the style of dependency specification used: project.json or <PackageReference> (By default vsimporter uses project.json). For projects using project.json the version needs to be updated locally. For projects using <PackageReference>, adding winobjc.packagereference.override.targets as an import to your project (an example can be found at bottom of WOCCatalog.vcxproj) will dynamically adjust the version of packages based on what is built locally and what git tags are present. Again for cross repo scenarios, it advisable to set WINOBJC_SDK_ROOT. See the below full example.

NOTE: Local packages are always preferred when using the override targets so make sure to not forget about locally produced packages.

Extremely Advanced and Extremely Detailed Example (Click to Expand)

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)

  3. 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 a nuget.config, Alices should consider adding one to control how nuget is configured for that solution). The full nuget.config shown as a diff would be:

      <?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 environment variable. See here for more info on nuget.config

  4. Now that her app can find the locally built packages, Alice needs to update the awesome app to depend on 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.Frameworks": "*",
      +         "WinObjC.Frameworks": "0.2.170229-dev-20170318003711"
               "WinObjC.Language": "*"
             },
             "frameworks": {
               "uap10.0": {
                 "imports": "native"
               }
             },
             "runtimes": {
               "win10-arm": {},
               "win10-x86": {},
               "win10-arm-aot": {},
               "win10-x86-aot": {}
             }
           }

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

      To help with this, Alice can use the Package Manager in Visual Studio or run Override-ProjectJson.ps1 (see here) from Powershell to update her project.json files:

       Override-ProjectJson.ps1 -ProjectJsonSearchPath C:\Path\To\Awesome\App\Repo
      
    • 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.

  5. Alice runs nuget restore on her Awesome App's solution and the new packages are installed. She rejoices.

    • If she is using project.json nuget restore happens automatically when building in Visual Studio. See nuget restore documentation.

    • If she is using <PackageReference>, she will need to run nuget restore from the command line. If she does not have a copy of nuget.exe, she can run init.cmd or init.ps1 located in the WinObjC repository root to get a copy at %WINOBJC_SDK_ROOT%.tools\

          %WINOBJC_SDK_ROOT%\.tools\nuget.exe restore \Path\to\Awesome\app.sln
      
  6. Bob has not cloned WinObjC but wants to try out Alice's awesome changes.

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

  8. Bob updates his nuget.config and project.json to look at these packages.

  9. Bob runs nuget restore and installs Alice's awesome packages.

  10. 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 WinObjC 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, building 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.

How can I consume WinObjC packages offline?

  1. Install WinObjC packages to a local folder:

      nuget.exe install WinObjC.Frameworks -OutputDirectory C:\Path\To\Where\You\Want\To\Store\Packages 
    

    NOTE: WinObjC.Frameworks depends on the rest of the package so this should be sufficient but if you want more fine grained control you can choose install specific versions of specific packages with the -Version command line argument. A full list of available WinObjC packages is here

    If you need a copy of nuget.exe you can see instructions here. It even supports Mac OSX and Linux!

  2. Set your local folder as a feed location. This can be done in the Visual Studio Nuget Package Manager settings or in a Nuget.Config file (shown below)

      <?xml version="1.0" encoding="utf-8"?>
      <configuration>
        <packageSources>
    +      <add key="Offline WinObjC Packages" value="C:\Path\To\Where\You\Installed\The\Packages" />
        </packageSources>
        <activePackageSource>
          <add key="All" value="(Aggregate source)" />
        </activePackageSource>
        <disabledPackageSources />
      </configuration>
  3. Profit. Nuget should now be configured to look locally for packages.

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).

Clone this wiki locally