Skip to content

Commit bdccb53

Browse files
authored
Merge pull request #36 from ryan-kipawa/build_improvements
Improve build system and update to 23.0.3
2 parents b65d596 + 883ea01 commit bdccb53

File tree

11 files changed

+288
-32
lines changed

11 files changed

+288
-32
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: Test Windows
22
on:
3-
push
3+
workflow_dispatch:
44

55
jobs:
66
build:

.github/workflows/full_test.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2+
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
3+
4+
name: Test Windows and Linux
5+
6+
on:
7+
push:
8+
branches: [ master]
9+
pull_request:
10+
branches: [ master ]
11+
12+
jobs:
13+
test:
14+
runs-on: ${{ matrix.os }}
15+
strategy:
16+
matrix:
17+
os: [ubuntu-latest, windows-latest]
18+
python-version: [3.8, 3.13]
19+
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- name: Install uv
24+
uses: astral-sh/setup-uv@v6
25+
with:
26+
version: "0.7.21"
27+
28+
- name: Set up Python ${{ matrix.python-version }}
29+
uses: actions/setup-python@v5
30+
with:
31+
python-version: ${{ matrix.python-version }}
32+
33+
# this is installed by default on Ubuntu 22.04
34+
- name: Install libnsl2 (Linux only)
35+
if: runner.os == 'Linux'
36+
run: |
37+
sudo apt-get update
38+
sudo apt-get install -y libnsl2
39+
40+
- name: Install the project
41+
run: uv sync
42+
43+
- name: Run tests
44+
run: uv run pytest
45+

.github/workflows/test_linux.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
name: Test Linux
22

33
on:
4-
push:
5-
branches: [ master ]
6-
pull_request:
7-
branches: [ master ]
8-
94
workflow_dispatch:
105

116
jobs:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,4 @@ bin
9090
x64
9191
/tmp/
9292
/TestResults
93+
uv.lock

README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,33 @@ This library is the foundation for [MIKE IO](https://github.com/DHI/mikeio).
1616
## Installation
1717

1818
```pip install mikecore```
19-
```
19+
20+
## Development
21+
22+
All commands are run from the project root.
23+
24+
1. **Sync & Build**
25+
```bash
26+
uv sync
27+
# Use 'uv sync --reinstall' to force-rebuild native components.
28+
```
29+
30+
2. **Update EUM Types** (for new release, or whenever EUM.xml changes)
31+
```bash
32+
# Generate definitions from the new native build
33+
uv run ./buildUtil/eumXMLProcess.py > eumItemUnit.txt
34+
35+
# Use a diff tool to merge changes into mikecore/eum.py.
36+
```
37+
38+
3. **Run Tests**
39+
```bash
40+
uv run pytest
41+
```
42+
43+
4. **Build Packages** (Optional)
44+
```bash
45+
uv build
46+
```
2047

2148

buildUtil/build.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
from __future__ import annotations
2+
3+
import shutil
4+
import urllib.request
5+
import xml.etree.ElementTree as ET
6+
import zipfile
7+
from pathlib import Path
8+
from typing import Any
9+
import subprocess
10+
import platform
11+
12+
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
13+
14+
15+
def download_nuget_package(
16+
package_id: str,
17+
version: str,
18+
output_dir: str | Path
19+
) -> None:
20+
"""
21+
Download a NuGet package (.nupkg) to output_dir.
22+
"""
23+
package_id_lower: str = package_id.lower()
24+
version_lower: str = version.lower()
25+
url: str = f"https://api.nuget.org/v3-flatcontainer/{package_id_lower}/{version_lower}/{package_id_lower}.{version_lower}.nupkg"
26+
output_dir = Path(output_dir)
27+
nupkg_path: Path = output_dir / f"{package_id}.{version}.nupkg"
28+
extract_path: Path = output_dir / f"{package_id}"
29+
30+
output_dir.mkdir(parents=True, exist_ok=True)
31+
32+
print(f"Downloading {package_id} {version} from {url}...")
33+
with urllib.request.urlopen(url) as response, nupkg_path.open("wb") as f:
34+
f.write(response.read())
35+
36+
print(f"Extracting {nupkg_path} to {extract_path}...")
37+
with zipfile.ZipFile(nupkg_path, "r") as zip_ref:
38+
zip_ref.extractall(extract_path)
39+
print("Done.")
40+
41+
def copy_native_libs_to_bin(packages_dir: str | Path, bin_dir: str | Path) -> None:
42+
"""
43+
Copy native shared libraries from NuGet packages to bin directory.
44+
"""
45+
packages_dir = Path(packages_dir)
46+
bin_windows = Path(bin_dir) / "windows"
47+
bin_linux = Path(bin_dir) / "linux"
48+
bin_windows.mkdir(parents=True, exist_ok=True)
49+
bin_linux.mkdir(parents=True, exist_ok=True)
50+
51+
for package in packages_dir.iterdir():
52+
win_native = package / "runtimes" / "win-x64" / "native"
53+
linux_native = package / "runtimes" / "linux-x64" / "native"
54+
55+
if win_native.exists():
56+
for lib in win_native.glob("*"):
57+
dest = bin_windows / lib.name
58+
print(f"Copying {lib} to {dest}")
59+
shutil.copy2(lib, dest)
60+
61+
if linux_native.exists():
62+
for lib in linux_native.glob("*"):
63+
dest = bin_linux / lib.name
64+
print(f"Copying {lib} to {dest}")
65+
shutil.copy2(lib, dest)
66+
67+
def read_packages_config(filepath: str | Path) -> list[tuple[str, str]]:
68+
"""
69+
Reads NuGet packages.config and returns a list of (id, version) tuples.
70+
"""
71+
tree = ET.parse(filepath)
72+
root = tree.getroot()
73+
return [
74+
(pkg.attrib["id"], pkg.attrib["version"])
75+
for pkg in root.findall("package")
76+
]
77+
78+
def modify_linux_so_rpath(bin_folder: str | Path):
79+
patchelf_path = shutil.which("patchelf")
80+
for so in Path(bin_folder).glob("*.so*"):
81+
print(f"Setting RUNPATH for {so} to '$ORIGIN'")
82+
subprocess.run(
83+
[patchelf_path, "--set-rpath", "$ORIGIN", str(so.absolute())],
84+
check=True,
85+
)
86+
87+
def setup():
88+
"""Setup function to download NuGet packages and copy native libraries into bin folder.
89+
"""
90+
packages = read_packages_config("buildUtil/packages.config")
91+
for name, version in packages:
92+
download_nuget_package(name, version, output_dir="packages")
93+
copy_native_libs_to_bin("packages", "mikecore/bin")
94+
95+
if platform.system().lower() == "linux":
96+
modify_linux_so_rpath("mikecore/bin/linux")
97+
98+
99+
class BuildHook(BuildHookInterface):
100+
"""Custom build hook to run setup during the build process."""
101+
102+
def initialize(self, version: str, build_data: dict[str : Any]) -> None:
103+
"""Initialize the build hook."""
104+
setup()
105+
106+
if __name__ == "__main__":
107+
setup()

buildUtil/eumXMLProcess.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import re
1313

1414
# Using readlines()
15-
eumFile = open('build/lib/mikecore/bin/windows/EUM.xml', 'r')
15+
eumFile = open('mikecore/bin/windows/EUM.xml', 'r')
1616
eumLines = eumFile.readlines()
1717
eumFile.close()
1818

buildUtil/packages.config

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<packages>
3+
<package id="DHI.DFS" version="23.0.3" />
4+
<package id="DHI.PFS" version="23.0.3" />
5+
<package id="DHI.EUM" version="23.0.3" />
6+
<package id="DHI.DHIfl" version="23.0.3" />
7+
<package id="DHI.Projections" version="23.0.3" />
8+
<package id="DHI.MikeCore.Linux.ubuntu22" version="23.0.3" />
9+
</packages>

mikecore/eum.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,50 @@ class eumItem(IntEnum):
605605
eumIBladeThickness = 110318
606606
eumIPlantDensity = 110319
607607
eumIThickness = 110320
608+
eumICarbonEmmisionFactor = 110321
609+
eumIAluminiumAl = 110322
610+
eumIBismuthBi = 110323
611+
eumICalciumCa = 110324
612+
eumICadmiumCd = 110325
613+
eumICobaltCo = 110326
614+
eumIChromiumCr = 110327
615+
eumICopperCu = 110328
616+
eumIIronFe = 110329
617+
eumIPotassiumK = 110330
618+
eumILithiumLi = 110331
619+
eumIMagnesiumMg = 110332
620+
eumIManganeseMn = 110333
621+
eumISodiumNa = 110334
622+
eumINickelNi = 110335
623+
eumILeadPb = 110336
624+
eumIStrontiumSr = 110337
625+
eumIZincZn = 110338
626+
eumISilverAg = 110339
627+
eumIBariumBa = 110340
628+
eumIBerylliumBe = 110341
629+
eumIIndiumIn = 110342
630+
eumIVanadiumV = 110343
631+
eumIArsenicAs = 110344
632+
eumISulfurS = 110345
633+
eumIMolybdenumMo = 110346
634+
eumIPhosphorusP = 110347
635+
eumIMercuryHg = 110348
636+
eumIAntimonySb = 110349
637+
eumISeleniumSe = 110350
638+
eumIUraniumU = 110351
639+
eumIBoronB = 110352
640+
eumIOxidationReductionPotentialORP = 110353
641+
eumITotalSuspendedSolidsTSS = 110354
642+
eumITotalDissolvedSolidsTDS = 110355
643+
eumIChlorideCl = 110356
644+
eumITurbidityNTU = 110357
645+
eumISulphateSO4 = 110358
646+
eumIPhosphatePO4 = 110359
647+
eumINitrateNO3 = 110360
648+
eumINitriteNO2 = 110361
649+
eumIFluorideF = 110362
650+
eumITotalAlkanlinity = 110363
651+
eumIChemicalOxygenDemandCOD = 110364
608652

609653
# Predefined enums of EUM units.
610654
#
@@ -886,6 +930,7 @@ class eumUnit(IntEnum):
886930
eumUTonPerYear = 4218
887931
eumUTonPerDay = 4219
888932
eumUTonPerSec = 4220
933+
eumUmilligramPerMin = 4221
889934
eumUgramPerM2 = 4400
890935
eumUkilogramPerM = 4401
891936
eumUkilogramPerM2 = 4402
@@ -1024,6 +1069,8 @@ class eumUnit(IntEnum):
10241069
eumUexaJoule = 5608
10251070
eumUmegaWattHour = 5609
10261071
eumUgigaWattHour = 5610
1072+
eumUkilogramPerKiloWattHour = 5630
1073+
eumUpoundPerKiloWattHour = 5631
10271074
eumUperJoule = 5650
10281075
eumUperKiloJoule = 5651
10291076
eumUperMegaJoule = 5652
@@ -1124,6 +1171,17 @@ class eumUnit(IntEnum):
11241171
eumUMgalUKPerDayPerPsi = 7508
11251172
eumUacftPerDayPerPsi = 7509
11261173
eumUm3PerHourPerBar = 7510
1174+
eumUliterPerSecPerMeter2OneHalf = 7530
1175+
eumUliterPerMinPerMeter2OneHalf = 7531
1176+
eumUMegaLiterPerDayPerMeter2OneHalf = 7532
1177+
eumUm3PerHourPerMeter2OneHalf = 7533
1178+
eumUm3PerDayPerMeter2OneHalf = 7534
1179+
eumUft3PerSecPerPsi2OneHalf = 7535
1180+
eumUgallonPerMinPerPsi2OneHalf = 7536
1181+
eumUMgalPerDayPerPsi2OneHalf = 7537
1182+
eumUMgalUKPerDayPerPsi2OneHalf = 7538
1183+
eumUacftPerDayPerPsi2OneHalf = 7539
1184+
eumUm3PerHourPerBar2OneHalf = 7540
11271185
eumUKilogramPerS2 = 8100
11281186
eumUm2Perkilogram = 9100
11291187
eumUPerMeterPerSecond = 9200

pyproject.toml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
[build-system]
2+
requires = ["hatchling", "patchelf; sys_platform == 'linux'"]
3+
build-backend = "hatchling.build"
4+
5+
[tool.hatch.build.hooks.custom]
6+
path = "buildUtil/build.py"
7+
8+
[tool.hatch.build.targets.sdist.force-include]
9+
"mikecore/bin" = "mikecore/bin"
10+
11+
[tool.hatch.build.targets.wheel.force-include]
12+
"mikecore/bin" = "mikecore/bin"
13+
14+
[project]
15+
name = "mikecore"
16+
version = "0.3.0a0"
17+
description = "MIKE Core contains core libraries, like DFS (Data File System), EUM and more."
18+
readme = "README.md"
19+
license = { file = "LICENSE.txt" }
20+
authors = [
21+
{ name = "DHI", email = "mike@dhigroup.com" }
22+
]
23+
requires-python = ">=3.5"
24+
dependencies = [
25+
"numpy"
26+
]
27+
classifiers = [
28+
"License :: OSI Approved :: BSD License",
29+
"Programming Language :: Python :: 3"
30+
]
31+
32+
[project.urls]
33+
Homepage = "https://github.com/DHI/mikecore-python"
34+
35+
[dependency-groups]
36+
dev = [
37+
"pytest>=6.1.2",
38+
]

0 commit comments

Comments
 (0)