google / or-tools

Google's Operations Research tools:
https://developers.google.com/optimization/
Apache License 2.0
11.26k stars 2.13k forks source link

Windows net452 framework is broken #1862

Closed GuyBenhaim closed 4 years ago

GuyBenhaim commented 4 years ago

Details: Version: 7.5 from NuGet, Windows 10, C#, Routing Solver,

I built the project and when calling it (via URL) the code reached the part that initializes the IndexManager, with valid parameters, and gets an immediate exception:

RoutingIndexManager manager = new RoutingIndexManager(number_of_locations, number_of_vehicles, vehicle_starts_, vehicle_ends_);
**<InnerException>**
<Message>An error has occurred.</Message>
<ExceptionMessage>
 'SWIGExceptionHelper'.
</ExceptionMessage>
<ExceptionType>System.TypeInitializationException</ExceptionType>
<StackTrace>
ב- Google.OrTools.ConstraintSolver.operations_research_constraint_solverPINVOKE.SWIGExceptionHelper..ctor() ב- Google.OrTools.ConstraintSolver.operations_research_constraint_solverPINVOKE..cctor()
</StackTrace>
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Cannot load DLL 'google-ortools-native'. Access is denied. (HRESULT: 0x80070005 (E_ACCESSDENIED))
</ExceptionMessage>
<ExceptionType>System.DllNotFoundException</ExceptionType>
<StackTrace>
Google.OrTools.ConstraintSolver.operations_research_constraint_solverPINVOKE.SWIGExceptionHelper.SWIGRegisterExceptionCallbacks_operations_research_constraint_solver(ExceptionDelegate applicationDelegate, ExceptionDelegate arithmeticDelegate, ExceptionDelegate divideByZeroDelegate, ExceptionDelegate indexOutOfRangeDelegate, ExceptionDelegate invalidCastDelegate, ExceptionDelegate invalidOperationDelegate, ExceptionDelegate ioDelegate, ExceptionDelegate nullReferenceDelegate, ExceptionDelegate outOfMemoryDelegate, ExceptionDelegate overflowDelegate, ExceptionDelegate systemExceptionDelegate) ב- Google.OrTools.ConstraintSolver.operations_research_constraint_solverPINVOKE.SWIGExceptionHelper..cctor()
</StackTrace>
</InnerException>
</InnerException>
</Error>
GuyBenhaim commented 4 years ago

This was working fine with 6.X, can we duplicate what was done in that makefile? Any 7.X (0<X<5) that is expected to work?

Mizux commented 4 years ago

Which Target Framework Moniker are you using ? We moved to .Net Core around 6.X to 7.X IIRC

GuyBenhaim commented 4 years ago

@Mizux , For 6.X I was using .Net Framework 4.5.2 for 7.5 I'm using .Net Framework 4.7.2 I understood that from 7.4 both Core and .NetFramework are supported.

m0ddixx commented 4 years ago

Is a hotfix planned for this issue? I just have waited for 7.5 to fix the signing issue. Now its fixed but or-tools still seems unusable for .net web projects

Mizux commented 4 years ago

Currently investigating what's going wrong when the packages were built...

GuyBenhaim commented 4 years ago

Thx! Let me know if there is something I could/should do from my side. Guy

Mizux commented 4 years ago

seems to be an issue with net452 VrpStartsEnds.cs

// [START program]
// [START import]
using System;
using System.Collections.Generic;
using Google.OrTools.ConstraintSolver;
// [END import]

/// <summary>
///   Minimal TSP using distance matrix.
/// </summary>
public class VrpStartsEnds {
  // [START data_model]
  class DataModel {
    public long[,] DistanceMatrix = {
      {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662},
      {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210},
      {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754},
      {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358},
      {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244},
      {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708},
      {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480},
      {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856},
      {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514},
      {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468},
      {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354},
      {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844},
      {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730},
      {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536},
      {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194},
      {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798},
      {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0}
    };
    public int VehicleNumber = 4;
    // [START starts_ends]
    public int[] Starts = {1, 2, 15, 16};
    public int[] Ends = {0, 0, 0, 0};
    // [END starts_ends]
  };
  // [END data_model]

  // [START solution_printer]
  /// <summary>
  ///   Print the solution.
  /// </summary>
  static void PrintSolution(
      in DataModel data,
      in RoutingModel routing,
      in RoutingIndexManager manager,
      in Assignment solution) {
    // Inspect solution.
    long maxRouteDistance = 0;
    for (int i = 0; i < data.VehicleNumber; ++i) {
      Console.WriteLine("Route for Vehicle {0}:", i);
      long routeDistance = 0;
      var index = routing.Start(i);
      while (routing.IsEnd(index) == false) {
        Console.Write("{0} -> ", manager.IndexToNode((int)index));
        var previousIndex = index;
        index = solution.Value(routing.NextVar(index));
        routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0);
      }
      Console.WriteLine("{0}", manager.IndexToNode((int)index));
      Console.WriteLine("Distance of the route: {0}m", routeDistance);
      maxRouteDistance = Math.Max(routeDistance, maxRouteDistance);
    }
    Console.WriteLine("Maximum distance of the routes: {0}m", maxRouteDistance);
  }
  // [END solution_printer]

  public static void Main(String[] args) {
    // Instantiate the data problem.
    // [START data]
    DataModel data = new DataModel();
    // [END data]

    // Create Routing Index Manager
    // [START index_manager]
    RoutingIndexManager manager = new RoutingIndexManager(
        data.DistanceMatrix.GetLength(0),
        data.VehicleNumber,
        data.Starts,
        data.Ends);
    // [END index_manager]

    // Create Routing Model.
    // [START routing_model]
    RoutingModel routing = new RoutingModel(manager);
    // [END routing_model]

    // Create and register a transit callback.
    // [START transit_callback]
    int transitCallbackIndex = routing.RegisterTransitCallback(
      (long fromIndex, long toIndex) => {
        // Convert from routing variable Index to distance matrix NodeIndex.
        var fromNode = manager.IndexToNode(fromIndex);
        var toNode = manager.IndexToNode(toIndex);
        return data.DistanceMatrix[fromNode, toNode]; }
    );
    // [END transit_callback]

    // Define cost of each arc.
    // [START arc_cost]
    routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);
    // [END arc_cost]

    // Add Distance constraint.
    // [START distance_constraint]
    routing.AddDimension(transitCallbackIndex, 0, 2000,
                         true,  // start cumul to zero
                         "Distance");
    RoutingDimension distanceDimension = routing.GetMutableDimension("Distance");
    distanceDimension.SetGlobalSpanCostCoefficient(100);
    // [END distance_constraint]

    // Setting first solution heuristic.
    // [START parameters]
    RoutingSearchParameters searchParameters =
      operations_research_constraint_solver.DefaultRoutingSearchParameters();
    searchParameters.FirstSolutionStrategy =
      FirstSolutionStrategy.Types.Value.PathCheapestArc;
    // [END parameters]

    // Solve the problem.
    // [START solve]
    Assignment solution = routing.SolveWithParameters(searchParameters);
    // [END solve]

    // Print solution on console.
    // [START print_solution]
    PrintSolution(data, routing, manager, solution);
    // [END print_solution]
  }
}
// [END program]

VrpStartsEnds.csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <LangVersion>7.3</LangVersion>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <EnableDefaultItems>false</EnableDefaultItems>
    <!-- see https://github.com/dotnet/docs/issues/12237 -->
    <RollForward>LatestMajor</RollForward>
    <RestoreSources>$(RestoreSources);https://api.nuget.org/v3/index.json</RestoreSources>
    <AssemblyName>Google.OrTools.VrpStartsEnds</AssemblyName>
    <IsPackable>true</IsPackable>
  </PropertyGroup>

  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugType>full</DebugType>
    <Optimize>true</Optimize>
    <GenerateTailCalls>true</GenerateTailCalls>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="VrpStartsEnds.cs" />
    <PackageReference Include="Google.OrTools" Version="7.5.*" />
  </ItemGroup>
</Project>

it's working....

if I change in VrpStartsEnds.csproj:

-     <TargetFramework>netcoreapp2.1</TargetFramework>
+     <TargetFramework>net452</TargetFramework>

observed

dotnet nuget locals all --clear
dotnet build
dotnet run

Unhandled Exception: System.TypeInitializationException: The type initializer for 'Google.OrTools.ConstraintSolver.operations_research_constraint_solverPINVOKE' threw an exception. ---> System.TypeInitializationException: The type initializer for 'SWIGExceptionHelper' threw an exception. ---> System.DllNotFoundException: Unable to l
oad DLL 'google-ortools-native': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
   at Google.OrTools.ConstraintSolver.operations_research_constraint_solverPINVOKE.SWIGExceptionHelper.SWIGRegisterExceptionCallbacks_operations_research_constraint_solver(ExceptionDelegate applicationDelegate, ExceptionDelegate arithmeticDelegate, ExceptionDelegate divideByZeroDelegate, ExceptionDelegate indexOutOfRangeDelegate, Ex
ceptionDelegate invalidCastDelegate, ExceptionDelegate invalidOperationDelegate, ExceptionDelegate ioDelegate, ExceptionDelegate nullReferenceDelegate, ExceptionDelegate outOfMemoryDelegate, ExceptionDelegate overflowDelegate, ExceptionDelegate systemExceptionDelegate)
   at Google.OrTools.ConstraintSolver.operations_research_constraint_solverPINVOKE.SWIGExceptionHelper..cctor()
   --- End of inner exception stack trace ---
   at Google.OrTools.ConstraintSolver.operations_research_constraint_solverPINVOKE.SWIGExceptionHelper..ctor()
   at Google.OrTools.ConstraintSolver.operations_research_constraint_solverPINVOKE..cctor()
   --- End of inner exception stack trace ---
   at Google.OrTools.ConstraintSolver.operations_research_constraint_solverPINVOKE.new_RoutingIndexManager__SWIG_1(Int32 jarg1, Int32 jarg2, Int32 length3, Int32[] jarg3, Int32 length4, Int32[] jarg4)
   at VrpStartsEnds.Main(String[] args) in C:\Users\corentinl\work\1862\VrpStartsEnds.cs:line 91
m0ddixx commented 4 years ago

what happens if you put <TargetFramework>net452;netcoreapp2.1</TargetFramework> I'd suggest it will find a compromise between the 2 sdk versions

Mizux commented 4 years ago

if I copy .nuget\packages\google.ortools.runtime.win-x64\7.5.7466\runtimes\win-x64\native\google-ortools-native.dll in bin\Debug\net452\google-ortools-native.dll it works

So it seems in net452 we are missing something...

note: on the contrary we have a bin\Debug\netcoreapp2.1\Google.OrTools.VrpStartsEnds.deps.json with

     "Google.OrTools.runtime.win-x64/7.5.7466": {
        "runtimeTargets": {
          "runtimes/win-x64/native/google-ortools-native.dll": {
            "rid": "win-x64",
            "assetType": "native",
            "fileVersion": "0.0.0.0"
          }
        }
      },
Mizux commented 4 years ago

@m0ddixx you'll need to use <TargetFrameworks> and basically it compile two times...

m0ddixx commented 4 years ago

oh right. if you fix the issue, is there a chance you upload it to the nuget repository or do we need to wait for the 7.6 release? Latter would mean I have to reschedule some work for my project which would be very helpful to be answered. Thanks in advance for your help

GuyBenhaim commented 4 years ago

In the proj file I've got: v4.7.2 But also:

..\packages\Google.OrTools.7.5.7466\lib\**net452**\Google.OrTools.dll
<Reference Include="Google.Protobuf, Version=3.11.2.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604, processorArchitecture=MSIL">
  <HintPath>..\packages\Google.Protobuf.3.11.2\lib\**net45**\Google.Protobuf.dll</HintPath>
</Reference>
GuyBenhaim commented 4 years ago

I tried building as 4.5.2 and ORT 7.5

The project builds, but after running it the program immediately crashes when reaching: RoutingIndexManager manager = new RoutingIndexManager(number_of_locations, number_of_vehicles, vehiclestarts, vehicleends);

With exception message: Access is denied. (- HRESULT: 0x80070005 (E_ACCESSDENIED))

...

Mizux commented 4 years ago

Managed to make it work using:

diff --git a/ortools/dotnet/Google.OrTools.runtime.win-x64/Google.OrTools.runtime.win-x64.csproj.in b/ortools/dotnet/Google.OrTools.runtime.win-x64/Google.OrTools.runtime.win-x64.csproj.in
index 2b7692853..fc187fe9e 100644
--- a/ortools/dotnet/Google.OrTools.runtime.win-x64/Google.OrTools.runtime.win-x64.csproj.in
+++ b/ortools/dotnet/Google.OrTools.runtime.win-x64/Google.OrTools.runtime.win-x64.csproj.in
@@ -29,13 +29,14 @@
       <Pack>true</Pack>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-
-    <!-- For netframework we need to copy the native binary manually during build on Nuget consumer side. That's why we need a separate target file doing this for us. -->
-    <Content Include="$(MSBuildThisFileDirectory)\Google.OrTools.runtime.win-x64.targets">
-      <PackagePath>build/net45/%(Filename)%(Extension)</PackagePath>
+    <!-- Except for net452 TFM see #1862 -->
+    <Content Include="../../../lib/$(AssemblyName).dll">
+      <PackagePath>lib/net452/%(Filename)%(Extension)</PackagePath>
       <Pack>true</Pack>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+
+    <!-- For netframework we need to copy the native binary manually during build on Nuget consumer side. That's why we need a separate target file doing this for us. -->
     <Content Include="$(MSBuildThisFileDirectory)\Google.OrTools.runtime.win-x64.targets">
        <PackagePath>build/netstandard2.0/%(Filename)%(Extension)</PackagePath>
        <Pack>true</Pack>

related to: https://docs.microsoft.com/en-us/nuget/create-packages/supporting-multiple-target-frameworks#framework-version-folder-structure

GuyBenhaim commented 4 years ago

Great news. Can you please explain the steps to take?

Mizux commented 4 years ago

ugly hack using a powershell: First you must know the location of your nuget package cache using:

dotnet nuget locals all --list

Then you can hack the google.ortools.runtime.win-x64 package :smiley_cat:

cd .nuget\packages\google.ortools.runtime.win-x64\7.5.7466
mkdir -p lib/net452
cp .\runtimes\win-x64\native\google-ortools-native.dll .\lib\net452\
GuyBenhaim commented 4 years ago

Where do I do this?

GuyBenhaim commented 4 years ago

I copied google-ortools-native.dll from here: packages\Google.OrTools.runtime.win-x64.7.5.7466\runtimes\win-x64\native to here: \packages\Google.OrTools.7.5.7466\lib\net452

Same exception ....

Mizux commented 4 years ago

you must do it in the nuget cache, did you try packages\Google.OrTools.runtime.win-x64.7.5.7466\lib\net452

GuyBenhaim commented 4 years ago

There is no \lib there Only directories: build, content, runtimes

I have lib under \packages\Google.OrTools.7.5.7466\lib\net452

GuyBenhaim commented 4 years ago

I cleared the nuget cache. nothing there. Still, getting the exception

m0ddixx commented 4 years ago

There is no \lib there Only directories: build, content, runtimes

I have lib under \packages\Google.OrTools.7.5.7466\lib\net452

You should create the lib folder and copy the dll from the runtime directory. I couldn't test this yet. I have to verify this tomorrow.

GuyBenhaim commented 4 years ago

I copied the native dll from: packages\Google.OrTools.runtime.win-x64.7.5.7466\runtimes\win-x64\native to" .nuget\packages\google.ortools.runtime.win-x64\7.5.7466\lib\net452

Same exception

GuyBenhaim commented 4 years ago

Tomorrow. BB.

ptr1120 commented 4 years ago

Can you add:

<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

to your csproj file. Due to any change in the nuget the native dll google-ortools-native.dll is not copied to output directory anymore. The above setting should fix that.

ConsoleApp1.zip

GuyBenhaim commented 4 years ago

Peter, What do you mean by "output directory" ? At least for me, manually copying google-ortools-native.dll to the nuget cache did not help in avoiding the exception. Thx

ptr1120 commented 4 years ago

I mean the build output directory, where all files go during build. Did you also try my suggestion?

m0ddixx commented 4 years ago

For me it is not working. At least for the ASP.Net Project.

Can you add:

<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

to your csproj file. Due to any change in the nuget the native dll google-ortools-native.dll is not copied to output directory anymore. The above setting should fix that.

ConsoleApp1.zip

GuyBenhaim commented 4 years ago

you mean adding to .csproj?

m0ddixx commented 4 years ago

i tried the solution by @Mizux with and without the added line to the .csproj file with no success

GuyBenhaim commented 4 years ago

Same here: added to .csproj -> same exception ....

ptr1120 commented 4 years ago

Yes, add this line to csproj tp the first PropertyGroup. Please also look at my uploaded example Project ConsoleApp1.zip. This is a NetFramework Console Project with this setting in csproj, which works for me. For the other users with problems: What is the failure message? Did you try the uploaded ConsoleApp1.zip and is it working on your side? Did you try to create a new Project with installing newest ortools in order to exclude side-effects in your specific configuration?

m0ddixx commented 4 years ago

I think OP and I are using ortools within a web application. I could test this with a console application, but that wouldn't fix the issue right away

GuyBenhaim commented 4 years ago

In my case the main project is a ClassLibrary and uses webAPI. The or-tools example-projects build and run for me, no issues. The problem is with the WebProject.

GuyBenhaim commented 4 years ago

Interestingly running your project crashes immediately when reaching : RoutingIndexManager manager = new RoutingIndexManager( data.DistanceMatrix.GetLength(0), data.VehicleNumber, data.Starts, data.Ends);

Exception Details:

System.TypeInitializationException HResult=0x80131534 Message=The type initializer for 'Google.OrTools.ConstraintSolver.operations_research_constraint_solverPINVOKE' threw an exception. Source=Google.OrTools StackTrace: at Google.OrTools.ConstraintSolver.operations_research_constraint_solverPINVOKE.new_RoutingIndexManager__SWIG_1(Int32 jarg1, Int32 jarg2, Int32 length3, Int32[] jarg3, Int32 length4, Int32[] jarg4) at Google.OrTools.ConstraintSolver.RoutingIndexManager..ctor(Int32 num_nodes, Int32 num_vehicles, Int32[] starts, Int32[] ends) at ConsoleApp1.Program.Main(String[] args) in C:\Users\guy\Documents\Temp\ConsoleApp1\ConsoleApp1\ConsoleApp1\Program.cs:line 91

Inner Exception 1: TypeInitializationException: The type initializer for 'SWIGExceptionHelper' threw an exception.

Inner Exception 2: DllNotFoundException: Unable to load DLL 'google-ortools-native': The specified module could not be found. (Exception from HRESULT: 0x8007007E)

GuyBenhaim commented 4 years ago

Doing VS "Clean Solution" for your project, now allows it to run. Nice.

But doing the same for mine, does not help ...

GuyBenhaim commented 4 years ago

This is the path in csproj:

..\packages\Google.OrTools.7.5.7466\lib\net452\Google.OrTools.dll I copied again manually ...native.dll to this directory. Same problem.
Mizux commented 4 years ago

Rereading #1500 and https://docs.microsoft.com/en-us/nuget/create-packages/supporting-multiple-target-frameworks#architecture-specific-folders I'm still not convincing if:

  1. Using a Target File to copy the google-ortools-native.dll was a right move
  2. Since our native google-ortools-native.dll is intended to be used by a net452 TFM should we put it in runtimes/win-x64/lib/net452/ instead of runtimes/win-x64/native. i.e. does native is not for C++ nuget package which is not exactly our case https://github.com/google/or-tools/blob/858fa626959f7e386153af82756384b79f983b5a/ortools/dotnet/Google.OrTools.runtime.win-x64/Google.OrTools.runtime.win-x64.csproj.in#L27-L28
  3. Why when using netcorapp2.1 or netcoreapp3.1 as TFM it magically work and do the right thing (i.e. copying the google-ortools-native.dll in the bin folder)
Mizux commented 4 years ago

@ptr1120 I'm pretty sure I'm wrong, but your App csproj seems cheating to me.

I mean at the end of "ConsoleApp1.csproj", we can show:

  <Import Project="..\packages\Google.OrTools.runtime.win-x64.7.5.7466\build\net45\Google.OrTools.runtime.win-x64.targets" Condition="Exists('..\packages\Google.OrTools.runtime.win-x64.7.5.7466\build\net45\Google.OrTools.runtime.win-x64.targets')" />
  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
    <PropertyGroup>
      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
    </PropertyGroup>
    <Error Condition="!Exists('..\packages\Google.OrTools.runtime.win-x64.7.5.7466\build\net45\Google.OrTools.runtime.win-x64.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Google.OrTools.runtime.win-x64.7.5.7466\build\net45\Google.OrTools.runtime.win-x64.targets'))" />
  </Target>

so it seems you "manually" call the .targets file while we would like for users to be able to consume it by simply adding in its .csproj:

    <PackageReference Include="Google.OrTools" Version="7.5.*" />

note: also your sample provide a packages.config and contains <HintPath>..\packages\Google.OrTools.7.5.7466\lib\net452\Google.OrTools.dll</HintPath> and the property CopyLocalLockFileAssemblies

ptr1120 commented 4 years ago

Yes, this (Import, Target) is automatically added to the csproj by nuget when you install the nuget package. The only change I did manually is the <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> setting.

Also when updating the ortools using the default nuget mechanism the Target and Import is updated automatically.

Mizux commented 4 years ago

@ptr1120 <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> don't seems to work using dotnet build.

I did few test using:

Concerning my point 1 above, in runtime linux-x64 and macos-x64 we don't use a target file shenanigan and while targeting netcoreapp2.1 or netcoreapp3.1 native file are still copied e.g. bin/Debug/netcoreapp3.1/runtimes/win-x64/google-ortools-native.dll (ed it also copy runtimes/linux-x64 and runtime/mac-x64) -> If I remove the build directory from nupkg it still copy files -> it seems it won't use the .target So for netstandard2.0 (aka netcoreapp2.1) and netstandard2.1 (aka netcoreapp3.1) it seems unneeded

Concerning 2 i guess we should keep the runtimes/<platform>-x64/native

BUT when I target net452 it won't work so I would tend to say runtimes/* stuff is something only working for .net core answering my 3

Now back to net452, i was only able to see them in bin/Debug/net452 while I was adding in google-runtime-win-x64 a lib/net452/google-native....dll (and in this test I also removed the build directory)

GuyBenhaim commented 4 years ago

How do we move forward from here?

ptr1120 commented 4 years ago

I will also have a deeper look on that. But first some implications:

Also I would like to summarize that there are no issues consuming ortools from netcore, netstandard and netframework, except for netframework web projects. Or did I understand anything wrong.

So, as a next step it would be interesting whether ortools work in a fresh, minimal netframework web project, created with VisualStudio and ortools installed with visual studio.

@Mizux: you are right the target file is only for netframework, which works only on windows, for dotnetcore on other platforms this is not needed because netcore has a completely different assembly resolving strategy.

ptr1120 commented 4 years ago

Interesting. Using the default mechanisms, OrTools seems to work also with a netframework Web Project. What I did:

namespace WebApplication1.Controllers { internal class DataModel { public long[,] DistanceMatrix = { {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0} };

    public int VehicleNumber = 4;

    // [START starts_ends]
    public int[] Starts = { 1, 2, 15, 16 };

    public int[] Ends = { 0, 0, 0, 0 };
    // [END starts_ends]
};

public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewBag.Title = "Home Page";
        ViewBag.Solution = Execute();

        return View();
    }

    private static string Execute()
    {
        // Instantiate the data problem.
        // [START data]
        DataModel data = new DataModel();
        // [END data]

        // Create Routing Index Manager
        // [START index_manager]
        RoutingIndexManager manager = new RoutingIndexManager(
            data.DistanceMatrix.GetLength(0),
            data.VehicleNumber,
            data.Starts,
            data.Ends);
        // [END index_manager]

        // Create Routing Model.
        // [START routing_model]
        RoutingModel routing = new RoutingModel(manager);
        // [END routing_model]

        // Create and register a transit callback.
        // [START transit_callback]
        int transitCallbackIndex = routing.RegisterTransitCallback(
          (long fromIndex, long toIndex) =>
          {
              // Convert from routing variable Index to distance matrix NodeIndex.
              var fromNode = manager.IndexToNode(fromIndex);
              var toNode = manager.IndexToNode(toIndex);
              return data.DistanceMatrix[fromNode, toNode];
          }
        );
        // [END transit_callback]

        // Define cost of each arc.
        // [START arc_cost]
        routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);
        // [END arc_cost]

        // Add Distance constraint.
        // [START distance_constraint]
        routing.AddDimension(transitCallbackIndex, 0, 2000,
                             true,  // start cumul to zero
                             "Distance");
        RoutingDimension distanceDimension = routing.GetMutableDimension("Distance");
        distanceDimension.SetGlobalSpanCostCoefficient(100);
        // [END distance_constraint]

        // Setting first solution heuristic.
        // [START parameters]
        RoutingSearchParameters searchParameters =
          operations_research_constraint_solver.DefaultRoutingSearchParameters();
        searchParameters.FirstSolutionStrategy =
          FirstSolutionStrategy.Types.Value.PathCheapestArc;
        // [END parameters]

        // Solve the problem.
        // [START solve]
        Assignment solution = routing.SolveWithParameters(searchParameters);
        // [END solve]

        // Print solution on console.
        // [START print_solution]
        return PrintSolution(data, routing, manager, solution);
        // [END print_solution]
    }

    private static string PrintSolution(
        in DataModel data,
        in RoutingModel routing,
        in RoutingIndexManager manager,
        in Assignment solution)
    {
        var sb = new StringBuilder();
        // Inspect solution.
        long maxRouteDistance = 0;
        for (int i = 0; i < data.VehicleNumber; ++i)
        {
            sb.AppendLine($"Route for Vehicle {i}:");
            Console.WriteLine();
            long routeDistance = 0;
            var index = routing.Start(i);
            while (routing.IsEnd(index) == false)
            {
                sb.Append($"{manager.IndexToNode((int)index)} -> ");
                var previousIndex = index;
                index = solution.Value(routing.NextVar(index));
                routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0);
            }
            sb.AppendLine($"{manager.IndexToNode((int)index)}");
            sb.AppendLine($"Distance of the route: {routeDistance}m");
            maxRouteDistance = Math.Max(routeDistance, maxRouteDistance);
        }
        sb.AppendLine($"Maximum distance of the routes: {maxRouteDistance}m");
        return sb.ToString();
    }
}

}



Result:
![Capture](https://user-images.githubusercontent.com/582627/73589983-56074680-44dd-11ea-80c0-562d454051b2.PNG)

I even did **not** add the `<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>` setting to csproj.
The `google-ortools-native.dll` is added to the output (bin/) directory during build automatically.

I add the project here, please can anybody confirm that this is working on your side.
[WebApplication1.zip](https://github.com/google/or-tools/files/4142380/WebApplication1.zip)

As a next step, after we have confirmed that default netcore/netframework (also Web) projects work with OrTools 7.5, we can check which of your specific project configuration is breaking the things.
ptr1120 commented 4 years ago

2. Since our native google-ortools-native.dll is intended to be used by a net452 TFM should we put it in runtimes/win-x64/lib/net452/ instead of runtimes/win-x64/native. i.e. does native is not for C++ nuget package which is not exactly our case https://github.com/google/or-tools/blob/858fa626959f7e386153af82756384b79f983b5a/ortools/dotnet/Google.OrTools.runtime.win-x64/Google.OrTools.runtime.win-x64.csproj.in#L27-L28

@Mizux: Under the runtime folder in a NuGet package, runtime dependent libraries are added. I believe lib folders are only for managed libraries (dotnet dll's) and all files in that folder will be referenced by the project at runtime. Whereas native folders contain non-managed (non-dotnet, e.g. c++) dependencies, which must not be referenced by the project, but needed at runtime (so must be copied to output directory).

GuyBenhaim commented 4 years ago

Hello, Any recommendation regarding ASP.Net Framework 4.5/4.7 web projects? Did you try to build starting with a 'clean' ASP.Net VS project and then adding ortools?

ptr1120 commented 4 years ago

@GuyBenhaim: I tried netframework 4.8. Did you try my attached web project? You can try creating a clean solution with any target framework you want using the described steps and tell what happens.

GuyBenhaim commented 4 years ago

the attached one works?

ptr1120 commented 4 years ago

Yes, as described, it works.

GuyBenhaim commented 4 years ago

Looks better that what I saw until this, but does get! Build, and opens the browser, but gets: Could not find a part of the path ...\WebApplication1\WebApplication1\bin\roslyn\csc.exe'.