Skip to content

Commit 11867b7

Browse files
committed
imporved-doc/included example to build binary from source code in Ubuntu 22.04, python3.10
1 parent c4c9e04 commit 11867b7

File tree

1 file changed

+224
-26
lines changed

1 file changed

+224
-26
lines changed

docs/Notes-on-Packaging.md

+224-26
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,66 @@
11
# Notes on Packaging
22

33
## Background
4-
* The dnp3-python version 0.2.0 is an extension and repackaging of [pydnp3](https://github.com/ChargePoint/pydnp3) version 0.1.0
4+
5+
* The dnp3-python version 0.2.0 is an extension and repackaging of [pydnp3](https://github.com/ChargePoint/pydnp3)
6+
version 0.1.0
57
* The wrapper provides more user-friendly and out-of-the-box examples (under /src/dnp3demo)
6-
* The wrapper enables features to install from [The Python Package Index (Pypi)](https://pypi.org/),
8+
* The wrapper enables features to install from [The Python Package Index (Pypi)](https://pypi.org/),
79
while the original package requires installing from the source code.
810

911
## Overview
12+
1013
* While, building package from the source code is NOT needed when using the dnp3-python package,
1114
it is still required for packing for distribution (i.e., building wheel and publishing to pypi.)
1215
* The dependencies to build c++ binding code are cmake and pybind11.
13-
* CMake >= 2.8.12 is required.
14-
* a special version of pybind11 is used and located at /deps/pybind11.
15-
* The c++ source code is based on [opendnp3](https://github.com/automatak/dnp3),
16+
* CMake >= 2.8.12 is required.
17+
* a special version of pybind11 is used and located at /deps/pybind11.
18+
* The c++ source code is based on [opendnp3](https://github.com/automatak/dnp3),
1619
forked and version-pinned at /deps/dnp3.
1720
the binding code are located at /src and its sub-folders.
1821
* setup.py describes the packaging configuration. (tested with with python==3.8.13, setuptools==63.4.1)
19-
* to build and install the package locally, run `python setup.py install`
20-
* to build wheel, run `python setup.py bdist_wheel [--plat-name=manylinux1_x86_64]`
22+
* to build and install the package locally, run `python setup.py install`
23+
* to build wheel, run `python setup.py bdist_wheel [--plat-name=manylinux1_x86_64]`
24+
25+
## Notes on working with recurse-submodules
26+
27+
* In order to build the binary (and the wheel) from the source code, the dnp3-python relies on pin-versioned "dnp3"
28+
and "pybind11".
29+
* Note that both the aforementioned source code repo need to present locally at the `/deps`.
30+
* Both of them are included in git as submodules. (See [.gitmodules](../.gitmodules)). To include the submodule,
31+
use `git clone <dnp3-python repo> --recurse-submodules`. The `--recurse-submodules` option makes sure nested
32+
submodules are cloned. (Note that the submodule "dnp3" include another submodule "asio".)
33+
* If the submodules were not cloned at the beginning, run `git submodule update --init --recursive`.
34+
* To work with submodules, please see the following references:
35+
* [Submodules - Git](https://git-scm.com/book/en/v2/Git-Tools-Submodules))
36+
* [Pull latest changes for all git submodules](https://stackoverflow.com/questions/1030169/pull-latest-changes-for-all-git-submodules)
37+
* [How to create Git submodules in GitHub and GitLab by example](https://www.theserverside.com/blog/Coffee-Talk-Java-News-Stories-and-Opinions/How-to-add-submodules-to-GitHub-repos)
38+
* [Cleans and resets a git repo and its submodules - gists · GitHub](https://gist.github.com/nicktoumpelis/11214362)
39+
* [How to checkout old git commit including all submodules recursively?](https://stackoverflow.com/questions/15124430/how-to-checkout-old-git-commit-including-all-submodules-recursively)
2140

2241
## Notes on Building native Python + CPP-binding Python Package
42+
2343
#### Intro
44+
2445
* Need to build + package a python project with cpp-binding code.
2546
* Desired result
26-
* pip install <package_name:dnp3_python>
27-
* able to import cpp-binding modules, e.g., “from pydnp3 import opendnp3”
28-
* able to import native python code, e.g., “from dnp3_python.dnp3station import master_new”
47+
* pip install <package_name:dnp3_python>
48+
* able to import cpp-binding modules, e.g., “from pydnp3 import opendnp3”
49+
* able to import native python code, e.g., “from dnp3_python.dnp3station import master_new”
2950
* Note:
30-
* the package is a wrapper on “pydnp3” project by repackaging its cpp-binding functionality
31-
and extended/redesigned API in Python.
32-
* the package inherited “pydnp3” project’s root module naming,
33-
i.e., “pydnp3” for cpp-binding related functionality.
34-
* the extended method adopted root module naming, “dnp3_python”.
35-
(There is a discussion at the end of this memo about the reason for using different root module name.)
51+
* the package is a wrapper on “pydnp3” project by repackaging its cpp-binding functionality
52+
and extended/redesigned API in Python.
53+
* the package inherited “pydnp3” project’s root module naming,
54+
i.e., “pydnp3” for cpp-binding related functionality.
55+
* the extended method adopted root module naming, “dnp3_python”.
56+
(There is a discussion at the end of this memo about the reason for using different root module name.)
57+
3658
#### Ingredients
37-
* Reference: [Package Discovery and Namespace Packages](https://setuptools.pypa.io/en/latest/userguide/package_discovery.html)
59+
60+
*
61+
62+
Reference: [Package Discovery and Namespace Packages](https://setuptools.pypa.io/en/latest/userguide/package_discovery.html)
63+
3864
* Key configuration in `setup.py`
3965
```
4066
packages=find_namespace_packages(
@@ -74,21 +100,193 @@
74100
```
75101
76102
#### Key takeaways
77-
* Using find_namespace_packages to “automatically” find packages
103+
104+
* Using find_namespace_packages to “automatically” find packages
78105
(assuming defined sub-modules properly, i.e., with `__init__.py`)
79-
* Using trailing * to include submodules
106+
* Using trailing * to include submodules
80107
(e.g., dnp3_python* will include dnp3_python and dnp3_python/dnp3station)
81108
* Verifying with artifact structure (e.g., whl structure or tar structure)
82109
83110
#### Discussion:
111+
84112
* dnp3_python is a package mixed with cpp binding binary and native Python source code.
85113
* the cpp binding path is resolved by using dynamic binary (i.e., *.so file)
86114
* the name space is called “pydnp3”
87115
* To avoid namespace conflict, use different root namespace for python source code package.
88-
* e.g., at one point, the pacakge adopted the structure /src/pydnp3/dnp3station,
89-
with the attempt to achieve `from pydnp3.dnp3station.master_new import *`.
90-
As a result, it will create a “pydnp3/dnp3station” dir at the site-package path.
91-
* However, under the aforementioned structure, the cpp binding submodules are not resolvable,
92-
e.g., not able to achieve “from pydnp3 import opendnp3”.
93-
python will find the native “pydnp3/” first and ignore the package path linked to `*.so` file.
94-
116+
* e.g., at one point, the pacakge adopted the structure /src/pydnp3/dnp3station,
117+
with the attempt to achieve `from pydnp3.dnp3station.master_new import *`.
118+
As a result, it will create a “pydnp3/dnp3station” dir at the site-package path.
119+
* However, under the aforementioned structure, the cpp binding submodules are not resolvable,
120+
e.g., not able to achieve “from pydnp3 import opendnp3”.
121+
python will find the native “pydnp3/” first and ignore the package path linked to `*.so` file.
122+
123+
## Example: building binary from C++ source code
124+
125+
* Demo OS: Ubuntu 22.04 -- use [OsBoxes VM](https://www.osboxes.org/ubuntu/) in this demo.
126+
* Virtual Environment Tool (e.g., conda, virtualenv) -- use conda in this demo.
127+
* Python version 3.10.
128+
129+
#### prepared the source code (including submodules) for build
130+
131+
```bash
132+
$ git clone <dnp3-python-repo> --recurse-submodules
133+
Cloning into 'dnp3-python'...
134+
remote: Enumerating objects: 1633, done.
135+
remote: Counting objects: 100% (15/15), done.
136+
remote: Compressing objects: 100% (15/15), done.
137+
remote: Total 1633 (delta 2), reused 3 (delta 0), pack-reused 1618
138+
Receiving objects: 100% (1633/1633), 12.09 MiB | 16.00 MiB/s, done.
139+
Resolving deltas: 100% (1275/1275), done.
140+
Submodule 'deps/dnp3' (https://github.com/kefeimo/opendnp3.git) registered for path 'deps/dnp3'
141+
Submodule 'deps/pybind11' (https://github.com/Kisensum/pybind11.git) registered for path 'deps/pybind11'
142+
Cloning into '/home/kefei/project/dnp3-python/deps/dnp3'...
143+
remote: Enumerating objects: 96789, done.
144+
remote: Counting objects: 100% (2074/2074), done.
145+
remote: Compressing objects: 100% (834/834), done.
146+
remote: Total 96789 (delta 1296), reused 1674 (delta 1152), pack-reused 94715
147+
Receiving objects: 100% (96789/96789), 25.70 MiB | 2.01 MiB/s, done.
148+
Resolving deltas: 100% (68129/68129), done.
149+
Cloning into '/home/kefei/project/dnp3-python/deps/pybind11'...
150+
remote: Enumerating objects: 9840, done.
151+
remote: Total 9840 (delta 0), reused 0 (delta 0), pack-reused 9840
152+
Receiving objects: 100% (9840/9840), 3.46 MiB | 1.99 MiB/s, done.
153+
Resolving deltas: 100% (6648/6648), done.
154+
Submodule path 'deps/dnp3': checked out '7d84673d165a4a075590a5f146ed1a4ba35d4e49'
155+
Submodule 'deps/asio' (https://github.com/chriskohlhoff/asio.git) registered for path 'deps/dnp3/deps/asio'
156+
Cloning into '/home/kefei/project/dnp3-python/deps/dnp3/deps/asio'...
157+
remote: Enumerating objects: 62636, done.
158+
remote: Counting objects: 100% (669/669), done.
159+
remote: Compressing objects: 100% (269/269), done.
160+
remote: Total 62636 (delta 462), reused 513 (delta 400), pack-reused 61967
161+
Receiving objects: 100% (62636/62636), 25.34 MiB | 2.24 MiB/s, done.
162+
Resolving deltas: 100% (45783/45783), done.
163+
Submodule path 'deps/dnp3/deps/asio': checked out '28d9b8d6df708024af5227c551673fdb2519f5bf'
164+
Submodule path 'deps/pybind11': checked out '338d615e12ce41ee021724551841de3cbe0bc1df'
165+
Submodule 'tools/clang' (https://github.com/wjakob/clang-cindex-python3) registered for path 'deps/pybind11/tools/clang'
166+
Cloning into '/home/kefei/project/dnp3-python/deps/pybind11/tools/clang'...
167+
remote: Enumerating objects: 368, done.
168+
remote: Counting objects: 100% (13/13), done.
169+
remote: Compressing objects: 100% (10/10), done.
170+
remote: Total 368 (delta 3), reused 10 (delta 3), pack-reused 355
171+
Receiving objects: 100% (368/368), 159.34 KiB | 1.20 MiB/s, done.
172+
Resolving deltas: 100% (154/154), done.
173+
Submodule path 'deps/pybind11/tools/clang': checked out '6a00cbc4a9b8e68b71caf7f774b3f9c753ae84d5'
174+
```
175+
176+
#### install build-essential
177+
178+
```bash
179+
$ sudo apt-get update && sudo apt-get install build-essential
180+
Hit:1 http://us.archive.ubuntu.com/ubuntu jammy InRelease
181+
Hit:2 http://security.ubuntu.com/ubuntu jammy-security InRelease
182+
Hit:3 http://us.archive.ubuntu.com/ubuntu jammy-updates InRelease
183+
Hit:4 http://us.archive.ubuntu.com/ubuntu jammy-backports InRelease
184+
Reading package lists... Done
185+
Reading package lists... Done
186+
Building dependency tree... Done
187+
Reading state information... Done
188+
...
189+
```
190+
191+
#### create a conda environment called dnp3-sandbox (Python==3.10)
192+
193+
```bash
194+
$ conda create -n dnp3-sandbox python=3.10
195+
Collecting package metadata (current_repodata.json): done
196+
Solving environment: done
197+
198+
## Package Plan ##
199+
200+
environment location: /home/kefei/miniconda3/envs/dnp3-sandbox
201+
202+
added / updated specs:
203+
- python=3.10
204+
205+
206+
The following NEW packages will be INSTALLED:
207+
...
208+
```
209+
210+
#### activate the virtual environment and install required dependency for build (i.e., cmake)
211+
212+
```bash
213+
$ conda activate dnp3-sandbox
214+
(dnp3-sandbox) $ pip install cmake
215+
Collecting cmake
216+
Downloading cmake-3.25.0-py2.py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (23.7 MB)
217+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 23.7/23.7 MB 2.1 MB/s eta 0:00:00
218+
Installing collected packages: cmake
219+
Successfully installed cmake-3.25.0
220+
```
221+
222+
#### (at repo root path) run `python setup.py install`
223+
224+
Watch the artifact created at "build/" folder.
225+
e.g., `lib.linux-x86_64-cpython-310/pydnp3.cpython-310-x86_64-linux-gnu.so`
226+
227+
```bash
228+
running install
229+
/home/kefei/miniconda3/envs/dnp3-sandbox/lib/python3.10/site-packages/setuptools/command/install.py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.
230+
warnings.warn(
231+
/home/kefei/miniconda3/envs/dnp3-sandbox/lib/python3.10/site-packages/setuptools/command/easy_install.py:144: EasyInstallDeprecationWarning: easy_install command is deprecated. Use build and pip and other standards-based tools.
232+
warnings.warn(
233+
running bdist_egg
234+
running egg_info
235+
writing src/dnp3_python.egg-info/PKG-INFO
236+
writing dependency_links to src/dnp3_python.egg-info/dependency_links.txt
237+
writing entry points to src/dnp3_python.egg-info/entry_points.txt
238+
writing top-level names to src/dnp3_python.egg-info/top_level.txt
239+
reading manifest file 'src/dnp3_python.egg-info/SOURCES.txt'
240+
adding license file 'LICENSE'
241+
...
242+
Processing dnp3_python-0.2.3b2-py3.10-linux-x86_64.egg
243+
removing '/home/kefei/miniconda3/envs/dnp3-sandbox/lib/python3.10/site-packages/dnp3_python-0.2.3b2-py3.10-linux-x86_64.egg' (and everything under it)
244+
creating /home/kefei/miniconda3/envs/dnp3-sandbox/lib/python3.10/site-packages/dnp3_python-0.2.3b2-py3.10-linux-x86_64.egg
245+
Extracting dnp3_python-0.2.3b2-py3.10-linux-x86_64.egg to /home/kefei/miniconda3/envs/dnp3-sandbox/lib/python3.10/site-packages
246+
dnp3-python 0.2.3b2 is already the active version in easy-install.pth
247+
Installing dnp3demo script to /home/kefei/miniconda3/envs/dnp3-sandbox/bin
248+
249+
Installed /home/kefei/miniconda3/envs/dnp3-sandbox/lib/python3.10/site-packages/dnp3_python-0.2.3b2-py3.10-linux-x86_64.egg
250+
Processing dependencies for dnp3-python==0.2.3b2
251+
Finished processing dependencies for dnp3-python==0.2.3b2
252+
```
253+
254+
(View full log
255+
at [cmake-build-dnp3-python.log](https://gist.github.com/kefeimo/35b7536c235c31ba0abf0da7fa7c4125#file-cmake-build-dnp3-python-log))
256+
257+
#### run `python setup.py bdist_wheel --plat-name=manylinux1_x86_64` to build wheel
258+
259+
Watch the artifact created at "dist/" folder.
260+
e.g., `dnp3_python-0.2.3b2-cp310-cp310-manylinux1_x86_64.whl`
261+
262+
```bash
263+
$ python setup.py bdist_wheel --plat-name=manylinux1_x86_64
264+
running bdist_wheel
265+
running build
266+
running build_py
267+
running build_ext
268+
CMake Deprecation Warning at CMakeLists.txt:1 (cmake_minimum_required):
269+
Compatibility with CMake < 2.8.12 will be removed from a future version of
270+
CMake.
271+
272+
Update the VERSION argument <min> value or use a ...<max> suffix to tell
273+
CMake that the project does not need compatibility with older versions.
274+
...
275+
adding 'dnp3_python-0.2.3b2.dist-info/METADATA'
276+
adding 'dnp3_python-0.2.3b2.dist-info/WHEEL'
277+
adding 'dnp3_python-0.2.3b2.dist-info/entry_points.txt'
278+
adding 'dnp3_python-0.2.3b2.dist-info/top_level.txt'
279+
adding 'dnp3_python-0.2.3b2.dist-info/RECORD'
280+
removing build/bdist.linux-x86_64/wheel
281+
```
282+
283+
More information
284+
about [building wheels](https://wheel.readthedocs.io/en/stable/user_guide.html?highlight=bdist_wheel#building-wheels),
285+
[The Story of Wheel](https://wheel.readthedocs.io/en/stable/story.html?highlight=bdist_wheel#the-story-of-wheel),
286+
[manylinux](https://github.com/pypa/manylinux) and `--plat-name`
287+
in [Running setuptools commands](https://setuptools.pypa.io/en/latest/deprecated/commands.html).
288+
289+
## Useful Resource
290+
291+
* [Setup Tools Doc](https://setuptools.pypa.io/en/latest/)
292+
* [wheel Doc](https://wheel.readthedocs.io/en/stable/index.html)

0 commit comments

Comments
 (0)