README.md
1# Introduction
2
3This is the documentation page for the .NET Standard 2.0 wrapper of OR-Tools.
4
5This project aim to explain how you build a .Net native (for win-x64, linux-x64
6and osx-x64) nuget package using `dotnet` and few `.csproj`.
7
8## Table of Content
9
10* [Requirement](#requirement)
11* [Directory Layout](#directory-layout)
12* [Build](#build)
13 * [Build Process](#build-process)
14 * [Local Google.OrTools Package](#local-googleortools-package)
15 * [Building a runtime Google.OrTools Package](#building-local-runtime-googleortools-package)
16 * [Building a Local Google.OrTools Package](#building-local-googleortools-package)
17 * [Complete Google.OrTools Package](#complete-googleortools-package)
18 * [Building all runtime Google.OrTools Package](#building-all-runtime-googleortools-package)
19 * [Building a Complete Google.OrTools Package](#building-complete-googleortools-package)
20* [Examples](#examples)
21* [Appendices](#appendices)
22 * [Resources](#resources)
23 * [Issues](#issues)
24* [Misc](#misc)
25
26## Requirement
27
28The library is compiled against `netstandard2.0`, so you'll only need:
29
30* .Net Core SDK >= 3.1 LTS
31
32## Directory Layout
33
34* [`Google.OrTools.runtime.linux-x64`](Google.OrTools.runtime.linux-x64)
35Contains the .Net Standard 2.0 native project for the rid linux-x64.
36* [`Google.OrTools.runtime.osx-x64`](Google.OrTools.runtime.osx-x64)
37Contains the .Net Standard 2.0 native project for the rid osx-x64.
38* [`Google.OrTools.runtime.win-x64`](Google.OrTools.runtime.win-x64)
39Contains the .Net Standard 2.0 native project for the rid win-x64.
40* [`Google.OrTools`](Google.OrTools) Is the .Net Standard 2.0 meta-package which
41should depends on all previous available packages and contains the Reference
42Assembly.
43
44note: While Microsoft use `runtime-<rid>.Company.Project` for native libraries
45naming, it is very difficult to get ownership on it, so you should prefer to use
46`Company.Project.runtime-<rid>` instead since you can have ownership on
47`Company.*` prefix more easily.
48
49## Build
50
51Either use the CMake base build or the Makefile based build. The workflow is
52typically `make dotnet` which will build both C# and F# libraries package. The
53output will be placed in `<OR_ROOT>/temp_dotnet/packages` folder. All tests will
54be run based on this folder.
55
56### Build Process
57
58To Create a native dependent package we will split it in two parts: - A bunch of
59`Google.OrTools.runtime.{rid}.nupkg` packages for each
60[Runtime Identifier (RId)](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog)
61targeted. - A meta-package `Google.OrTools.nupkg` depending on each runtime
62packages.
63
64note: [`Microsoft.NetCore.App` packages](https://www.nuget.org/packages?q=Microsoft.NETCore.App)
65follow this layout.
66
67We have two use case scenario:
68
691. Locally, be able to build a Google.OrTools package which **only** target the
70 local `OS Platform`, i.e. building for only one
71 [Runtime Identifier (RID)](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog).
72 \
73 note: This is useful since the C++ build is a complex process for Windows,
74 Linux and MacOS. i.e. We don't support cross-compilation for the native
75 library generation.
76
772. Be able to create a complete cross-platform (ed. platform as multiple rid)
78 Google.OrTools package. \
79 i.e. First you generate each native Nuget package
80 (`Google.OrTools.runtime.{rid}.nupkg`) on each native architecture, then
81 copy paste these artifacts on one native machine to generate the
82 meta-package `Google.OrTools`.
83
84### Local Google.OrTools Package
85
86Let's start with scenario 1: Create a *Local* `Google.OrTools` package targeting **one**
87[Runtime Identifier (RID)](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog).
88<br>We would like to build a `Google.OrTools.nupkg` package which only depends
89on one `Google.OrTools.runtime.{rid}.nupkg` in order to work locally.
90
91The pipeline for `linux-x64` should be as follow:
92![Local Pipeline](doc/local_pipeline.svg)
93![Legend](doc/legend.svg)
94note: The pipeline will be similar for `osx-x64` and `win-x64` architecture,
95don't hesitate to look at the CI log.
96
97#### Building local runtime Google.OrTools Package
98
99disclaimer: We won't cover the C++ ortools library build. So first let's create
100the local `Google.OrTools.runtime.{rid}.nupkg` nuget package.
101
102Here some dev-note concerning this `Google.OrTools.runtime.{rid}.csproj`.
103* `AssemblyName` must be `Google.OrTools.dll` i.e. all {rid} projects **must**
104 generate an assembly with the **same** name (i.e. no {rid} in the name).
105 On the other hand package identifier will contain the {rid}...
106 ```xml
107 <RuntimeIdentifier>{rid}</RuntimeIdentifier>
108 <AssemblyName>Google.OrTools</AssemblyName>
109 <PackageId>Google.OrTools.runtime.{rid}</PackageId>
110 ```
111* Once you specify a `RuntimeIdentifier` then `dotnet build` or `dotnet build -r {rid}`
112 will behave identically (save you from typing it).
113 note: not the case if you use `RuntimeIdentifiers` (notice the 's')
114* It is [recommended](https://docs.microsoft.com/en-us/nuget/create-packages/native-packages)
115 to add the tag `native` to the
116 [nuget package tags](https://docs.microsoft.com/en-us/dotnet/core/tools/csproj#packagetags)
117 ```xml
118 <PackageTags>native</PackageTags>
119 ```
120* Specify the output target folder for having the assembly output in
121 `runtimes/{rid}/lib/netstandard2.0` in the nupkg
122 ```xml
123 <BuildOutputTargetFolder>runtimes/$(RuntimeIdentifier)/lib</BuildOutputTargetFolder>
124 ```
125 note: Every files with an extension different from `.dll` will be filter out
126 by nuget. \
127 note: dotnet/cli automatically add the `$(TargetFramework)` (i.e.
128 `netstandard2.0`) to the output path.
129* Add the native shared library to the nuget package in the repository
130 `runtimes/{rib}/native`. e.g. for linux-x64:
131 ```xml
132 <Content Include="*.so">
133 <PackagePath>runtimes/linux-x64/native/%(Filename)%(Extension)</PackagePath>
134 <Pack>true</Pack>
135 <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
136 </Content>
137 ```
138* Generate the runtime package to a defined directory (i.e. so later in meta
139 Google.OrTools package we will be able to locate it)
140 ```xml
141 <PackageOutputPath>{...}/packages</PackageOutputPath>
142 ```
143* Generate the Reference Assembly (but don't include it to this runtime nupkg !,
144 see below for explanation) using:
145 ```xml
146 <ProduceReferenceAssembly>true</ProduceReferenceAssembly>
147 ```
148
149Then you can generate the package using:
150```bash
151dotnet pack src/Google.OrTools.runtime.{rid}
152```
153note: this will automatically trigger the `dotnet build`.
154
155If everything good the package (located where your `PackageOutputPath` was
156defined) should have this layout:
157```
158{...}/packages/Google.OrTools.runtime.{rid}.nupkg:
159\- Google.OrTools.runtime.{rid}.nuspec
160\- runtimes
161 \- {rid}
162 \- lib
163 \- {framework}
164 \- Google.OrTools.dll
165 \- native
166 \- *.so / *.dylib / *.dll
167...
168```
169note: `{rid}` could be `linux-x64` and `{framework}` could be `netstandard2.0`
170
171tips: since nuget package are zip archive you can use `unzip -l <package>.nupkg`
172to study their layout.
173
174#### Building local Google.OrTools Package
175
176So now, let's create the local `Google.OrTools.nupkg` nuget package which will
177depend on our previous runtime package.
178
179Here some dev-note concerning this `Google.OrTools.csproj`.
180
181* This package is a meta-package so we don't want to ship an empty assembly file
182 : `xml <IncludeBuildOutput>false</IncludeBuildOutput>`
183* Add the previous package directory: `xml <RestoreSources>{...}/packages;
184 $(RestoreSources)</RestoreSources>`
185* Add dependency (i.e. `PackageReference`) on each runtime package(s) available:
186 `xml <ItemGroup Condition="Exists('{...}/packages/Google.OrTools.runtime.linux-x64.1.0.0.nupkg')">
187 <PackageReference Include="Google.OrTools.runtime.linux-x64" Version="1.0.0" />
188 </ItemGroup>` Thanks to the `RestoreSource` we can work locally with our just
189 builded package without the need to upload it on
190 [nuget.org](https://www.nuget.org/).
191* To expose the .Net Surface API the `Google.OrTools.csproj` must contains at
192 least one
193 [Reference Assembly](https://docs.microsoft.com/en-us/nuget/reference/nuspec#explicit-assembly-references)
194 of the previously rumtime package. `xml <Content
195 Include="../Google.OrTools.runtime.{rid}/bin/$(Configuration)/$(TargetFramework)/{rid}/ref/*.dll">
196 <PackagePath>ref/$(TargetFramework)/%(Filename)%(Extension)</PackagePath>
197 <Pack>true</Pack> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
198 </Content>`
199
200Then you can generate the package using:
201```bash
202dotnet pack src/Google.OrTools
203```
204
205If everything good the package (located where your `PackageOutputPath` was
206defined) should have this layout: `{...}/packages/Google.OrTools.nupkg: \-
207Google.OrTools.nuspec \- ref \- {framework} \- Google.OrTools.dll ...` note:
208`{framework}` could be `netstandard2.0`
209
210### Complete Google.OrTools Package
211
212Let's start with scenario 2: Create a *Complete* `Google.OrTools.nupkg` package
213targeting multiple
214[Runtime Identifier (RID)](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog).
215<br>We would like to build a `Google.OrTools.nupkg` package which depends on several
216`Google.OrTools.runtime.{rid}.nupkg`.
217
218The pipeline should be as follow: \
219note: This pipeline should be run on any architecture, provided you have
220generated the three architecture dependent `Google.OrTools.runtime.{rid}.nupkg`
221nuget packages. ![Full Pipeline](doc/full_pipeline.svg)
222![Legend](doc/legend.svg)
223
224#### Building All runtime Google.OrTools Package
225
226Like in the previous scenario, on each targeted OS Platform you can build the
227corresponding `Google.OrTools.runtime.{rid}.nupkg` package.
228
229Simply run on each platform `bash dotnet build src/Google.OrTools.runtime.{rid}
230dotnet pack src/Google.OrTools.runtime.{rid}` note: replace `{rid}` by the
231Runtime Identifier associated to the current OS platform.
232
233Then on one machine used, you copy all other packages in the `{...}/packages` so
234when building `Google.OrTools.csproj` we can have access to all package...
235
236#### Building Complete Google.OrTools Package
237
238This is the same step than in the previous scenario, since we "see" all runtime
239packages in `{...}/packages`, the project will depends on each of them.
240
241Once copied all runtime package locally, simply run:
242```bash
243dotnet build src/Google.OrTools
244dotnet pack src/Google.OrTools
245```
246
247## Examples
248
249The Test projects show examples of building applications with `netcoreapp3.1`.
250
251The F# example folder shows how to compile against the typical .NET Framework
252installed on machine.
253
254## Appendices
255
256Few links on the subject...
257
258.Net runtime can deduce library extension so don’t use a platform-specific
259library name in the `DllImport` statement.
260Instead, just use the library name itself, without any prefixes or suffixes,
261and rely on the runtime to find the appropriate library at runtime.\
262ref: [Mono `pinvoke#libraryname`](https://www.mono-project.com/docs/advanced/pinvoke/#library-names)
263
264## Resources
265
266Some issue related to this process
267* [`PackageReference` only support `TargetFramework` condition](https://docs.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#adding-a-packagereference-condition)
268* [Nuget needs to support dependencies specific to target runtime #1660](https://github.com/NuGet/Home/issues/1660)
269* [Improve documentation on creating native packages #238](https://github.com/NuGet/docs.microsoft.com-nuget/issues/238)
270
271### Runtime IDentifier (RID)
272
273* [.NET Core RID Catalog](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog)
274* [Creating native packages](https://docs.microsoft.com/en-us/nuget/create-packages/native-packages)
275* [Blog on Nuget Rid Graph](https://natemcmaster.com/blog/2016/05/19/nuget3-rid-graph/)
276
277### Target Framework Moniker (TFM)
278
279* [.NET TFM list](https://docs.microsoft.com/en-us/dotnet/standard/frameworks)
280* [.NET Standard implementation support](https://docs.microsoft.com/en-us/dotnet/standard/net-standard)
281
282
283### Reference on .csproj format
284
285* [Common MSBuild project properties](https://docs.microsoft.com/en-us/visualstudio/msbuild/common-msbuild-project-properties?view=vs-2017)
286* [MSBuild well-known item metadata](https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-well-known-item-metadata?view=vs-2017)
287* [Additions to the csproj format for .NET Core](https://docs.microsoft.com/en-us/dotnet/core/tools/csproj)
288
289## Misc
290
291Image has been generated using [plantuml](http://plantuml.com/):
292```bash
293plantuml -Tpng doc/{file}.dot
294```
295
296So you can find the dot source files in [doc](doc).
297