diff --git a/.github/workflows/full_test.yml b/.github/workflows/full_test.yml index 4051bba4..a3248379 100644 --- a/.github/workflows/full_test.yml +++ b/.github/workflows/full_test.yml @@ -12,9 +12,10 @@ on: jobs: build: - runs-on: windows-latest + runs-on: ${{ matrix.os }} strategy: matrix: + os: [ubuntu-latest, windows-latest] python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] steps: diff --git a/mikeio1d/__init__.py b/mikeio1d/__init__.py index 5828aa69..6043c684 100644 --- a/mikeio1d/__init__.py +++ b/mikeio1d/__init__.py @@ -1,7 +1,7 @@ -import clr import sys import os -from platform import architecture +import platform +import pythonnet # PEP0440 compatible formatted version, see: # https://www.python.org/dev/peps/pep-0440/ @@ -21,12 +21,21 @@ # __version__ = "0.4.0" -if "64" not in architecture()[0]: +if "64" not in platform.architecture()[0]: raise Exception("This library has not been tested for a 32 bit system.") mike_bin_path = os.path.join(os.path.dirname(__file__), "bin") sys.path.append(mike_bin_path) +is_linux = platform.system() == "Linux" +if is_linux: + import mikecore + + runtime_config = os.path.join(mike_bin_path, "DHI.Mike1D.Application.runtimeconfig.json") + pythonnet.load("coreclr", runtime_config=runtime_config) + +import clr + clr.AddReference("System") clr.AddReference("System.Runtime") clr.AddReference("System.Runtime.InteropServices") diff --git a/mikeio1d/bin/DHI.Mike1D.Application.runtimeconfig.json b/mikeio1d/bin/DHI.Mike1D.Application.runtimeconfig.json new file mode 100644 index 00000000..855ecac4 --- /dev/null +++ b/mikeio1d/bin/DHI.Mike1D.Application.runtimeconfig.json @@ -0,0 +1,12 @@ +{ + "runtimeOptions": { + "tfm": "netcoreapp3.1", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "3.1.0" + }, + "configProperties": { + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false + } + } +} \ No newline at end of file diff --git a/scripts/nuget_retriever.py b/scripts/nuget_retriever.py index 9ff5c2ed..2f9d8dc4 100644 --- a/scripts/nuget_retriever.py +++ b/scripts/nuget_retriever.py @@ -2,7 +2,10 @@ import shutil import urllib.request import zipfile +import platform + from glob import glob +from pathlib import Path class NuGetPackageInfo: @@ -36,7 +39,7 @@ class NuGetRetriever: nuget_dir_name = "nuget" # Directory where libraries will be installed - bin_dir_name = r"mikeio1d\bin" + bin_dir_name = os.path.join("mikeio1d", "bin") # Default version of DHI NuGet packages to retrieve version_default = "21.0.0" @@ -57,25 +60,42 @@ class NuGetRetriever: "NetTopologySuite", ] - version_map = {"GeoAPI": "1.7.4", "NetTopologySuite": "2.0.0"} + package_names_linux = [ + "DHI.MikeCore.Linux.rhel7", + ] + + version_map = { + "GeoAPI": "1.7.4", + "NetTopologySuite": "2.0.0", + "DHI.MikeCore.Linux.rhel7": "20.0.0", + } # Builds to include - include_builds = ["netstandard2.0", "net45", "net47", "win-x64"] + include_builds = { + "Windows": ["netstandard2.0", "net45", "net47", "win-x64"], + "Linux": ["netstandard2.0", "linux-x64"], + } # Files with these extensions copy - extensions = ["*.dll", "*.pfs", "*.ubg", "*.xml"] + extensions = ["*.dll", "*.pfs", "*.ubg", "*.xml", "*.so", "*.so.5"] def __init__(self, path=path_default, version=version_default): self.path = path self.version = version def install_packages(self): + self.refine_package_names_list() self.create_nuget_dir_if_needed() self.generate_package_infos() self.download_packages() self.extract_packages() self.copy_packages_to_bin() + def refine_package_names_list(self): + is_linux = platform.system() == "Linux" + if is_linux: + self.package_names += self.package_names_linux + def create_nuget_dir_if_needed(self): path = os.path.join(self.path, self.nuget_dir_name) if not os.path.exists(path): @@ -105,7 +125,8 @@ def extract_packages(self): for info in self.package_infos: with zipfile.ZipFile(info.zip_name, "r") as zip_ref: - zip_ref.extractall(f"./{info.dir_name}") + extract_dir = os.path.join(".", info.dir_name) + zip_ref.extractall(extract_dir) def copy_packages_to_bin(self): destination = os.path.join(self.path, self.bin_dir_name) @@ -115,12 +136,23 @@ def copy_packages_to_bin(self): files = self.create_file_list_to_copy() for source_file in files: - source_file_path_stripped = source_file.split(r"\nuget")[1] + source_file_path_stripped = self.strip_source_file_path(source_file) print(f" Copying file: {source_file_path_stripped}") _, file_name = os.path.split(source_file) destination_file = os.path.join(destination, file_name) shutil.copy2(source_file, destination_file) + def strip_source_file_path(self, file_path): + path = Path(file_path) + path_stripped = path.parts[-1] + for path_part in reversed(path.parts): + if path_part == path_stripped: + continue + if path_part == "nuget": + break + path_stripped = os.path.join(path_part, path_stripped) + return path_stripped + def create_file_list_to_copy(self): start_dir = os.path.join(self.path, self.nuget_dir_name) extensions = self.extensions @@ -140,7 +172,8 @@ def create_file_list_to_copy(self): def check_file_candidate(self, file_candidate): include_file = False - for build in self.include_builds: + builds = self.include_builds[platform.system()] + for build in builds: if build in file_candidate: include_file = True return include_file @@ -150,7 +183,7 @@ def install(version=version_default): """Installs NuGet packages into mikeio1d/bin folder""" cwd = os.getcwd() path, _ = os.path.split(os.path.join(cwd, __file__)) - path = os.path.normpath(os.path.join(path, "../")) + path = os.path.normpath(os.path.join(path, "..")) print("Installing DHI NuGet packages:") diff --git a/scripts/util_builder.py b/scripts/util_builder.py index 7962fad6..ea2b0e0e 100644 --- a/scripts/util_builder.py +++ b/scripts/util_builder.py @@ -2,6 +2,8 @@ import shutil import subprocess +from pathlib import Path + class UtilBuilder: """ @@ -9,21 +11,21 @@ class UtilBuilder: """ # Default path - path_default = ".\\" + path_default = f".{os.sep}" # Directory for utility package source code util_dir_name = "util" # Directory where libraries are build - build_dir_name = r"bin\Release\netstandard2.0" + build_dir_name = os.path.join("bin", "Release", "netstandard2.0") # Directory where libraries will be installed - bin_dir_name = r"mikeio1d\bin" + bin_dir_name = os.path.join("mikeio1d", "bin") # Utility libraries to build and install library_names = ["DHI.Mike1D.MikeIO"] - # Files with these extensions copy + # Files with these extensions to copy extensions = [".dll"] def __init__(self, path=path_default): @@ -40,7 +42,7 @@ def build_libraries(self): print(f" Building project: {project_file_name}\n") - command = ["dotnet.exe", "build", "--configuration", "Release", project_file_path] + command = ["dotnet", "build", "--configuration", "Release", project_file_path] subprocess.run(command) def copy_libraries_to_bin(self): @@ -51,12 +53,17 @@ def copy_libraries_to_bin(self): files = self.create_file_list_to_copy() for source_file in files: - source_file_path_stripped = source_file.split("\\")[-1] + source_file_path_stripped = self.strip_source_file_path(source_file) print(f" Copying file: {source_file_path_stripped}") _, file_name = os.path.split(source_file) destination_file = os.path.join(destination, file_name) shutil.copy2(source_file, destination_file) + def strip_source_file_path(self, file_path): + path = Path(file_path) + path_stripped = path.parts[-1] + return path_stripped + def create_file_list_to_copy(self): start_dir = os.path.join(self.path, self.util_dir_name) files = [] @@ -74,7 +81,7 @@ def build_and_install(): """Builds and installs MIKE IO 1D utilities into mikeio1d/bin folder""" cwd = os.getcwd() path, _ = os.path.split(os.path.join(cwd, __file__)) - path = os.path.normpath(os.path.join(path, "../")) + path = os.path.normpath(os.path.join(path, "..")) util_builder = UtilBuilder(path) util_builder.build_libraries() diff --git a/setup.py b/setup.py index 61dfd4f7..ea7d24c0 100644 --- a/setup.py +++ b/setup.py @@ -31,6 +31,7 @@ 'pythonnet>=3.0.0a2; python_version >= "3.7.0"', "numpy", "pandas", + 'mikecore; platform_system=="Linux"', ], extras_require={ "dev": [