diff --git a/doc/manual/source/python.rst b/doc/manual/source/python.rst index 27b7b7fb6..158005dfa 100644 --- a/doc/manual/source/python.rst +++ b/doc/manual/source/python.rst @@ -52,45 +52,45 @@ Here is some example code that is written in Python and that runs |ns3|, which i from ns import ns - ns.core.LogComponentEnable("UdpEchoClientApplication", ns.core.LOG_LEVEL_INFO) - ns.core.LogComponentEnable("UdpEchoServerApplication", ns.core.LOG_LEVEL_INFO) + ns.LogComponentEnable("UdpEchoClientApplication", ns.LOG_LEVEL_INFO) + ns.LogComponentEnable("UdpEchoServerApplication", ns.LOG_LEVEL_INFO) - nodes = ns.network.NodeContainer() + nodes = ns.NodeContainer() nodes.Create(2) - pointToPoint = ns.point_to_point.PointToPointHelper() - pointToPoint.SetDeviceAttribute("DataRate", ns.core.StringValue("5Mbps")) - pointToPoint.SetChannelAttribute("Delay", ns.core.StringValue("2ms")) + pointToPoint = ns.PointToPointHelper() + pointToPoint.SetDeviceAttribute("DataRate", ns.StringValue("5Mbps")) + pointToPoint.SetChannelAttribute("Delay", ns.StringValue("2ms")) devices = pointToPoint.Install(nodes) - stack = ns.internet.InternetStackHelper() + stack = ns.InternetStackHelper() stack.Install(nodes) - address = ns.internet.Ipv4AddressHelper() - address.SetBase(ns.network.Ipv4Address("10.1.1.0"), - ns.network.Ipv4Mask("255.255.255.0")) + address = ns.Ipv4AddressHelper() + address.SetBase(ns.Ipv4Address("10.1.1.0"), + ns.Ipv4Mask("255.255.255.0")) interfaces = address.Assign(devices) - echoServer = ns.applications.UdpEchoServerHelper(9) + echoServer = ns.UdpEchoServerHelper(9) serverApps = echoServer.Install(nodes.Get(1)) - serverApps.Start(ns.core.Seconds(1.0)) - serverApps.Stop(ns.core.Seconds(10.0)) + serverApps.Start(ns.Seconds(1.0)) + serverApps.Stop(ns.Seconds(10.0)) address = interfaces.GetAddress(1).ConvertTo() - echoClient = ns.applications.UdpEchoClientHelper(address, 9) - echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1)) - echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds(1.0))) - echoClient.SetAttribute("PacketSize", ns.core.UintegerValue(1024)) + echoClient = ns.UdpEchoClientHelper(address, 9) + echoClient.SetAttribute("MaxPackets", ns.UintegerValue(1)) + echoClient.SetAttribute("Interval", ns.TimeValue(ns.Seconds(1.0))) + echoClient.SetAttribute("PacketSize", ns.UintegerValue(1024)) clientApps = echoClient.Install(nodes.Get(0)) - clientApps.Start(ns.core.Seconds(2.0)) - clientApps.Stop(ns.core.Seconds(10.0)) + clientApps.Start(ns.Seconds(2.0)) + clientApps.Stop(ns.Seconds(10.0)) - ns.core.Simulator.Run() - ns.core.Simulator.Destroy() + ns.Simulator.Run() + ns.Simulator.Destroy() @@ -226,71 +226,71 @@ After installing it, you can start using ns-3 right away. For example, using the :: - from ns import ns + from ns import ns - ns.cppyy.cppdef(""" - using namespace ns3; + ns.cppyy.cppdef(""" + using namespace ns3; - Callback,const Address&,const Address&> - make_sinktrace_callback(void(*func)(Ptr,Address,Address)) - { - return MakeCallback(func); - } - """) + Callback,const Address&,const Address&> + make_sinktrace_callback(void(*func)(Ptr, const Address&,const Address&)) + { + return MakeCallback(func); + } + """) - # Define the trace callback - def SinkTracer(packet: ns.Packet, src_address: ns.Address, dst_address: ns.Address) -> None: - print(f"At {ns.Simulator.Now().GetSeconds():.0f}s, '{dst_address}' received packet" - f" with {packet.__deref__().GetSerializedSize()} bytes from '{src_address}'") + # Define the trace callback + def SinkTracer(packet: ns.Packet, src_address: ns.Address, dst_address: ns.Address) -> None: + print(f"At {ns.Simulator.Now().GetSeconds():.0f}s, '{dst_address}' received packet" + f" with {packet.__deref__().GetSerializedSize()} bytes from '{src_address}'") - # Create two nodes - csmaNodes = ns.network.NodeContainer() - csmaNodes.Create(2) + # Create two nodes + csmaNodes = ns.NodeContainer() + csmaNodes.Create(2) - # Connect the two nodes - csma = ns.csma.CsmaHelper() - csma.SetChannelAttribute("DataRate", ns.core.StringValue("100Mbps")) - csma.SetChannelAttribute("Delay", ns.core.TimeValue(ns.core.NanoSeconds(6560))) - csmaDevices = csma.Install(csmaNodes) + # Connect the two nodes + csma = ns.CsmaHelper() + csma.SetChannelAttribute("DataRate", ns.StringValue("100Mbps")) + csma.SetChannelAttribute("Delay", ns.TimeValue(ns.NanoSeconds(6560))) + csmaDevices = csma.Install(csmaNodes) - # Install the internet stack - stack = ns.internet.InternetStackHelper() - stack.Install(csmaNodes) + # Install the internet stack + stack = ns.InternetStackHelper() + stack.Install(csmaNodes) - # Assign Ipv4 addresses - address = ns.internet.Ipv4AddressHelper() - address.SetBase(ns.network.Ipv4Address("10.1.2.0"), ns.network.Ipv4Mask("255.255.255.0")) - csmaInterfaces = address.Assign(csmaDevices) + # Assign Ipv4 addresses + address = ns.Ipv4AddressHelper() + address.SetBase(ns.Ipv4Address("10.1.2.0"), ns.Ipv4Mask("255.255.255.0")) + csmaInterfaces = address.Assign(csmaDevices) - # Setup applications - echoServer = ns.applications.UdpEchoServerHelper(9) + # Setup applications + echoServer = ns.UdpEchoServerHelper(9) - serverApps = echoServer.Install(csmaNodes.Get(0)) - serverApps.Start(ns.core.Seconds(1.0)) - serverApps.Stop(ns.core.Seconds(10.0)) + serverApps = echoServer.Install(csmaNodes.Get(0)) + serverApps.Start(ns.Seconds(1.0)) + serverApps.Stop(ns.Seconds(10.0)) - echoClient = ns.applications.UdpEchoClientHelper(csmaInterfaces.GetAddress(0).ConvertTo(), 9) - echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(10)) - echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds(1.0))) - echoClient.SetAttribute("PacketSize", ns.core.UintegerValue(1024)) + echoClient = ns.UdpEchoClientHelper(csmaInterfaces.GetAddress(0).ConvertTo(), 9) + echoClient.SetAttribute("MaxPackets", ns.UintegerValue(10)) + echoClient.SetAttribute("Interval", ns.TimeValue(ns.Seconds(1.0))) + echoClient.SetAttribute("PacketSize", ns.UintegerValue(1024)) - clientApps = echoClient.Install(csmaNodes.Get(1)) - clientApps.Start(ns.core.Seconds(2.0)) - clientApps.Stop(ns.core.Seconds(10.0)) + clientApps = echoClient.Install(csmaNodes.Get(1)) + clientApps.Start(ns.Seconds(2.0)) + clientApps.Stop(ns.Seconds(10.0)) - # Populate routing tables - ns.internet.Ipv4GlobalRoutingHelper.PopulateRoutingTables() + # Populate routing tables + ns.Ipv4GlobalRoutingHelper.PopulateRoutingTables() - # Setup the trace callback - sinkTraceCallback = ns.cppyy.gbl.make_sinktrace_callback(SinkTracer) - serverApps.Get(0).__deref__().TraceConnectWithoutContext("RxWithAddresses", sinkTraceCallback); + # Setup the trace callback + sinkTraceCallback = ns.cppyy.gbl.make_sinktrace_callback(SinkTracer) + serverApps.Get(0).__deref__().TraceConnectWithoutContext("RxWithAddresses", sinkTraceCallback); - # Set the simulation duration to 11 seconds - ns.Simulator.Stop(ns.Seconds(11)) + # Set the simulation duration to 11 seconds + ns.Simulator.Stop(ns.Seconds(11)) - # Run the simulator - ns.Simulator.Run() - ns.Simulator.Destroy() + # Run the simulator + ns.Simulator.Run() + ns.Simulator.Destroy() Which should print: @@ -326,31 +326,32 @@ For example, when handling command-line arguments, we could set additional param .. sourcecode:: python - # Import the ns-3 C++ modules with Cppyy - from ns import ns + # Import the ns-3 C++ modules with Cppyy + from ns import ns + import sys - # To pass the addresses of the Python variables to c++, we need to use ctypes - from ctypes import c_bool, c_int, c_double, c_char_p, create_string_buffer - verbose = c_bool(True) - nCsma = c_int(3) - throughputKbps = c_double(3.1415) - BUFFLEN = 4096 - outputFileBuffer = create_string_buffer(b"default_output_file.xml", BUFFLEN) - outputFile = c_char_p(outputFileBuffer.raw) + # To pass the addresses of the Python variables to c++, we need to use ctypes + from ctypes import c_bool, c_int, c_double, c_char_p, create_string_buffer + verbose = c_bool(True) + nCsma = c_int(3) + throughputKbps = c_double(3.1415) + BUFFLEN = 4096 + outputFileBuffer = create_string_buffer(b"default_output_file.xml", BUFFLEN) + outputFile = c_char_p(outputFileBuffer.raw) - # Cppyy will transform the ctype types into the appropriate reference or raw pointers - cmd = ns.CommandLine(__file__) - cmd.AddValue("verbose", "Tell echo applications to log if true", verbose) - cmd.AddValue("nCsma", "Number of extra CSMA nodes/devices", nCsma) - cmd.AddValue("throughputKbps", "Throughput of nodes", throughputKbps) - cmd.AddValue("outputFile", "Output file name", outputFile, BUFFLEN) - cmd.Parse(sys.argv) + # Cppyy will transform the ctype types into the appropriate reference or raw pointers + cmd = ns.CommandLine(__file__) + cmd.AddValue("verbose", "Tell echo applications to log if true", verbose) + cmd.AddValue("nCsma", "Number of extra CSMA nodes/devices", nCsma) + cmd.AddValue("throughputKbps", "Throughput of nodes", throughputKbps) + cmd.AddValue("outputFile", "Output file name", outputFile, BUFFLEN) + cmd.Parse(sys.argv) - # Printing values of the different ctypes passed as arguments post parsing - print("Verbose:", verbose.value) - print("nCsma:", nCsma.value) - print("throughputKbps:", throughputKbps.value) - print("outputFile:", outputFile.value) + # Printing values of the different ctypes passed as arguments post parsing + print("Verbose:", verbose.value) + print("nCsma:", nCsma.value) + print("throughputKbps:", throughputKbps.value) + print("outputFile:", outputFile.value) Note that the variables are passed as references or raw pointers. Reassigning them on the Python side (e.g. ``verbose = verbose.value``) can result in the Python garbage collector destroying the object @@ -371,62 +372,63 @@ no bounds checking in CommandLine::AddValue variant for ``char*``. .. sourcecode:: python - from ns import ns - from ctypes import c_char_p, c_char, create_string_buffer, byref, cast + from ns import ns + from ctypes import c_char_p, c_char, create_string_buffer, byref, cast - # The following buffer represent the memory contents - # of a program containing two adjacent C strings - # This could be the result of two subsequent variables - # on the stack or dynamically allocated - memoryContents = create_string_buffer(b"SHORT_STRING_CONTENTS\0"+b"DoNotWriteHere_"*5+b"\0") - lenShortString = len(b"SHORT_STRING_CONTENTS\0") + # The following buffer represent the memory contents + # of a program containing two adjacent C strings + # This could be the result of two subsequent variables + # on the stack or dynamically allocated + memoryContents = create_string_buffer(b"SHORT_STRING_CONTENTS\0"+b"DoNotWriteHere_"*5+b"\0") + lenShortString = len(b"SHORT_STRING_CONTENTS\0") - # In the next lines, we pick pointers to these two C strings - shortStringBuffer = cast(byref(memoryContents, 0), c_char_p) - victimBuffer = cast(byref(memoryContents, lenShortString), c_char_p) + # In the next lines, we pick pointers to these two C strings + shortStringBuffer = cast(byref(memoryContents, 0), c_char_p) + victimBuffer = cast(byref(memoryContents, lenShortString), c_char_p) - cmd = ns.core.CommandLine(__file__) - # in the real implementation, the buffer size of 21+1 bytes containing SHORT_STRING_CONTENTS\0 is passed - cmd.AddValue("shortString", "", shortStringBuffer) + cmd = ns.CommandLine(__file__) + # in the real implementation, the buffer size of 21+1 bytes containing SHORT_STRING_CONTENTS\0 is passed + # we use the entire size of the memory contents for demonstration purposses + cmd.AddValue("shortString", "", shortStringBuffer, 75) - print("Memory contents before the memory corruption") - print("Full Memory contents", memoryContents.raw) - print("shortStringBuffer contents: ", shortStringBuffer.value) - print("victimBuffer contents: ", victimBuffer.value) + print("Memory contents before the memory corruption") + print("Full Memory contents", memoryContents.raw) + print("shortStringBuffer contents: ", shortStringBuffer.value) + print("victimBuffer contents: ", victimBuffer.value) - # The following block should print to the terminal. - # Note that the strings are correctly - # identified due to the null terminator (\x00) - # - # Memory contents before the memory corruption - # Full Memory contents b'SHORT_STRING_CONTENTS\x00DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_\x00\x00' - # shortStringBuffer size=21, contents: b'SHORT_STRING_CONTENTS' - # victimBuffer size=75, contents: b'DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_' + # The following block should print to the terminal. + # Note that the strings are correctly + # identified due to the null terminator (\x00) + # + # Memory contents before the memory corruption + # Full Memory contents b'SHORT_STRING_CONTENTS\x00DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_\x00\x00' + # shortStringBuffer size=21, contents: b'SHORT_STRING_CONTENTS' + # victimBuffer size=75, contents: b'DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_' - # Write a very long string to a small buffer of size lenShortString = 22 - cmd.Parse(["python", "--shortString="+("OkToWrite"*lenShortString)[:lenShortString]+"CORRUPTED_"*3]) + # Write a very long string to a small buffer of size lenShortString = 22 + cmd.Parse(["python", "--shortString="+("OkToWrite"*lenShortString)[:lenShortString]+"CORRUPTED_"*3]) - print("\n\nMemory contents after the memory corruption") - print("Full Memory contents", memoryContents.raw) - print("shortStringBuffer contents: ", shortStringBuffer.value) - print("victimBuffer contents: ", victimBuffer.value) + print("\n\nMemory contents after the memory corruption") + print("Full Memory contents", memoryContents.raw) + print("shortStringBuffer contents: ", shortStringBuffer.value) + print("victimBuffer contents: ", victimBuffer.value) - # The following block should print to the terminal. - # - # Memory contents after the memory corruption - # Full Memory contents b'OkToWriteOkToWriteOkToCORRUPTED_CORRUPTED_CORRUPTED_\x00oNotWriteHere_DoNotWriteHere_DoNotWriteHere_\x00\x00' - # shortStringBuffer size=52, contents: b'OkToWriteOkToWriteOkToCORRUPTED_CORRUPTED_CORRUPTED_' - # victimBuffer size=30, contents: b'CORRUPTED_CORRUPTED_CORRUPTED_' - # - # Note that shortStringBuffer invaded the victimBuffer since the - # string being written was bigger than the shortStringBuffer. - # - # Since no bounds checks were performed, the adjacent memory got - # overwritten and both buffers are now corrupted. - # - # We also have a memory leak of the final block in the memory - # 'oNotWriteHere_DoNotWriteHere_DoNotWriteHere_\x00\x00', caused - # by the null terminator written at the middle of the victimBuffer. + # The following block should print to the terminal. + # + # Memory contents after the memory corruption + # Full Memory contents b'OkToWriteOkToWriteOkToCORRUPTED_CORRUPTED_CORRUPTED_\x00oNotWriteHere_DoNotWriteHere_DoNotWriteHere_\x00\x00' + # shortStringBuffer size=52, contents: b'OkToWriteOkToWriteOkToCORRUPTED_CORRUPTED_CORRUPTED_' + # victimBuffer size=30, contents: b'CORRUPTED_CORRUPTED_CORRUPTED_' + # + # Note that shortStringBuffer invaded the victimBuffer since the + # string being written was bigger than the shortStringBuffer. + # + # Since no bounds checks were performed, the adjacent memory got + # overwritten and both buffers are now corrupted. + # + # We also have a memory leak of the final block in the memory + # 'oNotWriteHere_DoNotWriteHere_DoNotWriteHere_\x00\x00', caused + # by the null terminator written at the middle of the victimBuffer. If you find a segmentation violation, be sure to wait for the stacktrace provided by Cppyy and try to find the root cause of the issue. If you have multiple cores, the number of @@ -443,22 +445,22 @@ module (`ns-3-dev/bindings/python/ns__init__.py`). .. sourcecode:: python - # Redefine Time operators - cppyy.cppdef(""" - using namespace ns3; - bool Time_ge(Time& a, Time& b){ return a >= b;} - bool Time_eq(Time& a, Time& b){ return a == b;} - bool Time_ne(Time& a, Time& b){ return a != b;} - bool Time_le(Time& a, Time& b){ return a <= b;} - bool Time_gt(Time& a, Time& b){ return a > b;} - bool Time_lt(Time& a, Time& b){ return a < b;} - """) - cppyy.gbl.ns3.Time.__ge__ = cppyy.gbl.Time_ge - cppyy.gbl.ns3.Time.__eq__ = cppyy.gbl.Time_eq - cppyy.gbl.ns3.Time.__ne__ = cppyy.gbl.Time_ne - cppyy.gbl.ns3.Time.__le__ = cppyy.gbl.Time_le - cppyy.gbl.ns3.Time.__gt__ = cppyy.gbl.Time_gt - cppyy.gbl.ns3.Time.__lt__ = cppyy.gbl.Time_lt + # Redefine Time operators + cppyy.cppdef(""" + using namespace ns3; + bool Time_ge(Time& a, Time& b){ return a >= b;} + bool Time_eq(Time& a, Time& b){ return a == b;} + bool Time_ne(Time& a, Time& b){ return a != b;} + bool Time_le(Time& a, Time& b){ return a <= b;} + bool Time_gt(Time& a, Time& b){ return a > b;} + bool Time_lt(Time& a, Time& b){ return a < b;} + """) + cppyy.gbl.ns3.Time.__ge__ = cppyy.gbl.Time_ge + cppyy.gbl.ns3.Time.__eq__ = cppyy.gbl.Time_eq + cppyy.gbl.ns3.Time.__ne__ = cppyy.gbl.Time_ne + cppyy.gbl.ns3.Time.__le__ = cppyy.gbl.Time_le + cppyy.gbl.ns3.Time.__gt__ = cppyy.gbl.Time_gt + cppyy.gbl.ns3.Time.__lt__ = cppyy.gbl.Time_lt A different operator used by |ns3| is `operator Address()`, used to @@ -467,8 +469,8 @@ This is not supported by Cppyy and requires explicit conversion. .. sourcecode:: python - # Explicitly convert the InetSocketAddress to Address using InetSocketAddress.ConvertTo() - sink.Bind(ns.network.InetSocketAddress(ns.network.Ipv4Address.GetAny(), 80).ConvertTo()) + # Explicitly convert the InetSocketAddress to Address using InetSocketAddress.ConvertTo() + sink.Bind(ns.InetSocketAddress(ns.Ipv4Address.GetAny(), 80).ConvertTo()) Most of the missing APIs can be wrapped, given enough time, patience, and expertise, and will likely be wrapped if bug reports are submitted. However, don't file a bug report saying "bindings are incomplete", because the project does not have maintainers to maintain every API. @@ -487,11 +489,11 @@ In Python, the C++ std::ofstream has been minimally wrapped to allow this. For :: - ascii = ns.ofstream("wifi-ap.tr") # create the file - ns.YansWifiPhyHelper.EnableAsciiAll(ascii) - ns.Simulator.Run() - ns.Simulator.Destroy() - ascii.close() # close the file + ascii = ns.ofstream("wifi-ap.tr") # create the file + ns.YansWifiPhyHelper.EnableAsciiAll(ascii) + ns.Simulator.Run() + ns.Simulator.Destroy() + ascii.close() # close the file There is one caveat: you must not allow the file object to be garbage collected while |ns3| is still using it. That means that the 'ascii' variable above must not be allowed to go out of scope or else the program will crash. @@ -507,14 +509,14 @@ The python bindings are generated into an 'ns' namespace. Examples: :: from ns import ns - n1 = ns.network.Node() + n1 = ns.Node() or :: from ns import* - n1 = ns.network.Node() + n1 = ns.Node() The best way to explore the bindings is to look at the various example programs provided in |ns3|; some C++ examples have a corresponding Python @@ -616,23 +618,23 @@ ns-3 (e.g. libxml2, gsl, sqlite, gtk, etc) and for the bindings and packaging .. sourcecode:: yaml - # Install minimal toolchain - - yum install -y libxml2-devel gsl-devel sqlite-devel gtk3-devel boost-devel - # Create Python venv - - $PYTHON -m venv ./venv - - . ./venv/bin/activate - # Upgrade the pip version to reuse the pre-build cppyy - - $PYTHON -m pip install pip --upgrade - - $PYTHON -m pip install setuptools setuptools_scm --upgrade - - $PYTHON -m pip install wheel auditwheel cmake-build-extension cppyy + # Install minimal toolchain + - yum install -y libxml2-devel gsl-devel sqlite-devel gtk3-devel boost-devel + # Create Python venv + - $PYTHON -m venv ./venv + - . ./venv/bin/activate + # Upgrade the pip version to reuse the pre-build cppyy + - $PYTHON -m pip install pip --upgrade + - $PYTHON -m pip install setuptools setuptools_scm --upgrade + - $PYTHON -m pip install wheel auditwheel cmake-build-extension cppyy The project is then configured loading the configuration settings defined in the ``ns-3-dev/setup.py`` file. .. sourcecode:: yaml - # Configure and build wheel - - $PYTHON setup.py bdist_wheel build_ext + # Configure and build wheel + - $PYTHON setup.py bdist_wheel build_ext At this point, we have a wheel that only works in the current system, since external libraries are not shipped. @@ -645,9 +647,9 @@ by the script ``ns-3-dev/build-support/pip-wheel/auditwheel-exclude-list.py``. .. sourcecode:: yaml - - export EXCLUDE_INTERNAL_LIBRARIES=`$PYTHON ./build-support/pip-wheel/auditwheel-exclude-list.py` - # Bundle in shared libraries that were not explicitly packaged or depended upon - - $PYTHON -m auditwheel repair ./dist/*whl -L /lib64 $EXCLUDE_INTERNAL_LIBRARIES + - export EXCLUDE_INTERNAL_LIBRARIES=`$PYTHON ./build-support/pip-wheel/auditwheel-exclude-list.py` + # Bundle in shared libraries that were not explicitly packaged or depended upon + - $PYTHON -m auditwheel repair ./dist/*whl -L /lib64 $EXCLUDE_INTERNAL_LIBRARIES At this point, we should have our final wheel ready, but we need to check if it works @@ -657,48 +659,48 @@ We first clean the environment and uninstall the packages previously installed. .. sourcecode:: yaml - # Clean the build directory - - $PYTHON ./ns3 clean - # Clean up the environment - - deactivate - - rm -R ./venv - # Delete toolchain to check if required headers/libraries were really packaged - - yum remove -y libxml2-devel gsl-devel sqlite-devel gtk3-devel boost-devel + # Clean the build directory + - $PYTHON ./ns3 clean + # Clean up the environment + - deactivate + - rm -R ./venv + # Delete toolchain to check if required headers/libraries were really packaged + - yum remove -y libxml2-devel gsl-devel sqlite-devel gtk3-devel boost-devel Then we can install our newly built wheel and test it. .. sourcecode:: yaml - # Install wheel - - $PYTHON -m pip install ./wheelhouse/*whl - - $PYTHON -m pip install matplotlib numpy - # Test the bindings - - $PYTHON ./utils/python-unit-tests.py - - $PYTHON ./examples/realtime/realtime-udp-echo.py - - $PYTHON ./examples/routing/simple-routing-ping6.py - - $PYTHON ./examples/tutorial/first.py - - $PYTHON ./examples/tutorial/second.py - - $PYTHON ./examples/tutorial/third.py - - $PYTHON ./examples/wireless/wifi-ap.py - - $PYTHON ./examples/wireless/mixed-wired-wireless.py - - $PYTHON ./src/bridge/examples/csma-bridge.py - - $PYTHON ./src/brite/examples/brite-generic-example.py - - $PYTHON ./src/core/examples/sample-simulator.py - - $PYTHON ./src/core/examples/sample-rng-plot.py --not-blocking - - $PYTHON ./src/click/examples/nsclick-simple-lan.py - - $PYTHON ./src/flow-monitor/examples/wifi-olsr-flowmon.py - - $PYTHON ./src/flow-monitor/examples/flowmon-parse-results.py output.xml - - $PYTHON ./src/openflow/examples/openflow-switch.py + # Install wheel + - $PYTHON -m pip install ./wheelhouse/*whl + - $PYTHON -m pip install matplotlib numpy + # Test the bindings + - $PYTHON ./utils/python-unit-tests.py + - $PYTHON ./examples/realtime/realtime-udp-echo.py + - $PYTHON ./examples/routing/simple-routing-ping6.py + - $PYTHON ./examples/tutorial/first.py + - $PYTHON ./examples/tutorial/second.py + - $PYTHON ./examples/tutorial/third.py + - $PYTHON ./examples/wireless/wifi-ap.py + - $PYTHON ./examples/wireless/mixed-wired-wireless.py + - $PYTHON ./src/bridge/examples/csma-bridge.py + - $PYTHON ./src/brite/examples/brite-generic-example.py + - $PYTHON ./src/core/examples/sample-simulator.py + - $PYTHON ./src/core/examples/sample-rng-plot.py --not-blocking + - $PYTHON ./src/click/examples/nsclick-simple-lan.py + - $PYTHON ./src/flow-monitor/examples/wifi-olsr-flowmon.py + - $PYTHON ./src/flow-monitor/examples/flowmon-parse-results.py output.xml + - $PYTHON ./src/openflow/examples/openflow-switch.py If all programs finish normally, the bindings are working as expected, and will be saved as an artifact. .. sourcecode:: yaml - artifacts: - paths: - - wheelhouse/*.whl + artifacts: + paths: + - wheelhouse/*.whl One can use ``gitlab-ci-local`` to build the pip wheels locally. After that, the wheels will be stored in ``.gitlab-ci-local/artifacts/manylinux-pip-wheel-py3Lg10/wheelhouse`` @@ -730,7 +732,7 @@ Then install the wheel and its dependencies running the following command: .. sourcecode:: bash - $ pip install *.whl + $ pip install *.whl Publishing the pip wheel via Pypi *********************************