Compare commits

..

15 Commits

Author SHA1 Message Date
Tom Henderson e9ae18f809 bump VERSION number 2007-06-17 16:04:12 -07:00
Tom Henderson 396c76892c release_steps.txt document 2007-06-17 14:17:22 -07:00
Mathieu Lacage c329d7dd4e improve Packet Header/Trailer API dox 2007-06-16 17:15:58 +02:00
Tom Henderson ca3871e4f6 Backed out changeset 767 for the release (not ready) 2007-06-15 13:30:40 -07:00
Tom Henderson d1c6c9a348 back out unfinished p2p changes 2007-06-15 13:19:57 -07:00
Gustavo J. A. M. Carneiro d5260811dc Add a Packet::AddAtEnd unit test. 2007-06-15 18:10:40 +01:00
Gustavo J. A. M. Carneiro ce155b4d35 WAF: remove debugging print accidentally left in. 2007-06-15 15:25:01 +01:00
Gustavo J. A. M. Carneiro 65aca43f3f WAF: make all ns3 modules register themselves in the environment; link the run-tests program with all ns3 modules, not with a hardcoded list. 2007-06-15 15:19:38 +01:00
Gustavo J. A. M. Carneiro 96418f577d re-sync cairo-wideint.c with upstream cairo's version (also fixes the recent compiler warning, but in a different way) 2007-06-14 13:09:56 +01:00
Gustavo J. A. M. Carneiro 2ee9064175 merge 2007-06-14 12:55:24 +01:00
George F. Riley 4042681e83 Simplification to p2p-net-device and p2p-channel (work in progress) 2007-06-14 10:41:47 +02:00
Tom Henderson cdf13df87d Add main-query-interface.cc sample file 2007-06-13 23:13:21 -07:00
Gustavo J. A. M. Carneiro 50037ae611 Fix compilier warning/error: src/simulator/cairo-wideint.c:789: warning: comparison between signed and unsigned integer expressions. 2007-06-13 18:40:38 +01:00
Gustavo J. A. M. Carneiro e38244e7c0 WAF: add -Wall and -Werror to CXXFLAGS, like in the SCons build. 2007-06-13 18:34:05 +01:00
Mathieu Lacage 4f0eae4a6d add a rationale for the way Trailers work 2007-06-13 16:37:04 +02:00
3298 changed files with 40050 additions and 1367828 deletions
-56
View File
@@ -1,56 +0,0 @@
*.pyc
.waf*
src/NDNabstraction
build/
doc/html/
doc/latex/
\.rej$
\.orig$
\.o$
~$
^build-dir
^build
^testpy-output
^doc/html
^doc/latex
.lock-wscript
doc/ns3-object.txt
doc/introspected-doxygen.h
.lock-waf_darwin_build
.lock-waf_linux2_build
.lock-waf_cygwin_build
.lock-waf_freebsd7_build
.lock-waf_freebsd8_build
.lock-waf_freebsd9_build
.waf
^doc/introspected-doxygen\.h$
.*\.py[co]$
\.pcap$
\.mob$
\.routes$
^doc/manual/build
^doc/tutorial/build
^doc/testing/build
^doc/models/build
^doc/models/source-temp
^doc/manual/figures/.*eps
^doc/manual/figures/.*pdf
^doc/manual/figures/.*png
^bindings/python/pybindgen/
ms_print.*
massif.*
\.patch$
\.diff$
\.tr$
\#[^\#/]+\#$
.lock-wscript
.lock-wafbuild
src/ndnSIM
configure
toggle
.ns3rc
.cproject
.project
.settings/
+9 -48
View File
@@ -1,48 +1,9 @@
\.rej$
\.orig$
\.o$
~$
^build-dir
^build
^testpy-output
^doc/html
^doc/latex
^doc/ns3-object.txt
^\.lock-waf_darwin_build
^\.lock-waf_linux2_build
^\.lock-waf_cygwin_build
^\.lock-waf_freebsd7_build
^\.lock-waf_freebsd8_build
^\.lock-waf_freebsd9_build
^\.waf
^doc/introspected-doxygen\.h$
.*\.py[co]$
\.pcap$
\.mob$
\.routes$
^seventh-packet-byte-count.png
\.cwnd
^doc/doxygen.log$
^doc/doxygen.warnings.log$
^doc/(manual|models|tutorial|tutorial-pt-br)/build
^doc/(manual|models|tutorial|tutorial-pt-br)/figures/.*\.(eps|pdf|png)$
^doc/manual/source-temp
^doc/models/source-temp
^doc/ns3_html_theme/static/ns3_version.js$
^src/.*/doc/build
^src/.*/doc/source/figures/.*\.(eps|pdf)$
^src/.*/doc/.*\.png
^bindings/python/pybindgen/
ms_print.*
massif.*
\.patch$
\.diff$
\.tr$
\.dat$
\.plt$
\#[^\#/]+\#$
^coverity
^(D|U)l[A-Z][a-z]*Stats.txt$
syntax: glob
TAGS
waf
.*.orig$
.*\.o$
.*~$
build-dir
build
.*\.sconsign
doc/html.*
doc/latex.*
.lock-wscript
+2 -73
View File
@@ -1,73 +1,2 @@
56928998e05c9c11f5f3aefe79be8d2843e0db88 ns-3.0.1
7ac5a4b0969b255c4824c926c2b37ef450136ce9 ns-3.0.2
38099dd26e9467b8f49f8632f22789858149a6e7 ns-3.0.3
5701e60bf01a8ac1308945e69001e0cc07948faf ns-3.0.4
08046b6aef37932507696a2f2f427b42d693781e ns-3.0.5
606df29888e7573b825fc891a002f0757166b616 ns-3.0.6
36472385a1cc7c44d34fb7a5951b930010f4e8d2 ns-3.0.7
560a5091e0e6ded47d269e2f2dee780f13950a63 ns-3.0.8
4db981a0d9eb135e3e1c07765cff8d66f7a55cca ns-3.0.9
b5bf2588cde2f1273b1095cc5c83a0c272e55370 ns-3.0.10
ee5e1da76ecc52337f097cd90ebb50a3d49ec541 ns-3.0.11
b17f2928291eec5bf5b1c59a4a5fd583f704ac40 ns-3.0.12
79dba133b5f8a2d6f6f678a22e8519bc155e6a4e ns-3.0.13
8869a79a391f49d0c787f0558cc9329b4e7ee40e ns-3.1-RC1
951296e9a277e14113a0d3ba86a9688b222569ae ns-3.1-RC2
ea16c44eb90db579c83d3434fc8a960be506a7f5 ns-3.1-RC3
42504fb1f7be7817b8be513cdcd3d9bc8f3660e8 ns-3.1
5768685f9fdba9fbf2e9561a840f085142c73575 ns-3.1
dfd634417b8d1896d981b6f44d8f71030611596a ns-3.2-RC1
319eb29611b18998abbad01548825643a8017bcb ns-3.2-RC2
d783a951f8f5e64b33bc518f0415f76cae1ca6f3 ns-3.2-RC2-bis
fa1c7b813873cfa251be7d1b7cea38373fe82fa1 ns-3.2-RC3
68218c266a844f9fbda34a0ffddb1ae2adebd4b0 ns-3.2-RC4
2ecac911b3ec40d73ab8301471bea6d9ba5b9885 ns-3.2
f33cbf6b051c51d92db41b6fa825437958fbf801 ns-3.3-RC1
0000000000000000000000000000000000000000 ns-3.3-RC1
a84f2ab246e691a88b527a84ce21896b95080070 ns-3.3-RC1
654eed5f3ad03c04c2bcfb6852b5b4ae94260bc6 ns-3.3-RC2
a66553c56a8f14644af6c229c83bb0a653cb3f32 ns-3.3-RC3
dfd0bc16dc991313896f351530a3dc5a25f62e15 ns-3.3-RC4
58ae52c5845ff03ba08dc921e13e6cf3604c810a ns-3.3-RC5
4267fd454004f5a60e517496f43a01000ccebc72 ns-3.3-RC6
2efae18e73794c0acd8061a9bd5c74b69f1b0d93 ns-3.3
5396ecd09060bcb1e7b1f750bb170cbfde7a77c2 ns-3.4
13a5b15fbe3429a6c3d00c1bccffa2c156a0603b ns-3.5-rc1
8562a42accf6f715d312c037326ec7da48095e13 ns-3.5-rc2
a600c11ff8d40a40e88c2d692acad6512dde70c8 ns-3.5-rc3
c975274c9707b1f07d94cc51f205c351122131a5 ns-3.5
549243b47311211975b388cd64fcb9111caa2fc2 ns-3.6-RC1
8996042990466b1eda718a848e1c02923c0add74 ns-3.6-RC2
79ff6ad1adbb7b4677759ddf52028b68b0515168 ns-3.6-RC3
39a82d7a0d661febe42e75f26ada79424258e330 ns-3.6-RC4
d55c479666ac6be0575fac695ddf355c0530e0dd ns-3.6
892efc87a1518fb69b04628c779195aee139d33e ns-3.7-RC3
17bf6ee332baaa6b6f9b8a26d29c98f4e715648f ns-3.7-RC4
be3fb855c65a46d22a7693cd156f154f63602e8e ns-3.7
e61ce382fcdd5363a9d0052601c5eeae6f5e8d81 ns-3.8-RC1
3b7ec0d36079b22fbc8c0d86b8edb876bf0ae9d8 ns-3.8-RC2
d588e7fe6cb064919e2738e71e6169dc9be94e0b ns-3.8-RC3
533be42b3c7ff738d76517a9a6e48f1795f54844 ns-3.8-RC4
0ef062c0897e189f016951c307eaceedccad81e0 ns-3.8
fb5ad9c7755aa6ea871e76d322fd4c43a5b1047d ns-3.9-RC1
14019c625ab84486f0c263074048b0519db15f73 ns-3.9-RC2
def4153e27cd744f117acf8c509979617850da83 ns-3.9-RC3
8ddf25211f8995cde73baa8b8a419711ed451b85 ns-3.9
63a8a4ed4054f1e1cd1756045abba657f6fd884c ns-3.10
440bbee145f096193abcdd67fe6c16de62935d89 ns-3.11-RC1
0a7a16b599e86ca7884e5b5772bf7c5cfe146603 ns-3.11-RC2
e48ed3aabca6ad71c8c49e4604c0f83345eda6a8 ns-3.11-RC3
9843c12351cb5ceb9613c9db390d94073b713284 ns-3.11
19c9e2b33b4a53f200be80cd49810bdfecf76770 ns-3.12-RC1
167fc2274f53d4d89fdd769e4a7683ad7b6b7157 ns-3.12
9007193fbb8d18b69a1feb6aec16501dcf138b6f ns-3.13
ae540de68a2534213342c5e0b12afb47d7518a90 ns-3.14
26a511d6dbad7284e66de0f3b2ad5bebe6b76405 ns-3.15
49d343e55caec257bb01e400eef6c14522f37e1b ns-3.16
b4a70b99171ade6e9628a87780994238950a1df1 ns-3.17
cfbc9491d7e7c9d346cc042fd35b8afb8836e81f ns-3.18
322102df792e2be6c74df74f776b3470fb1db795 ns-3.19
da0eb48df23f96335f32a37f047a6bc27e197c8d ns-3.20
fa4be182ef17746cea50e89af557c79a28b76533 ns-3.21
103f62fc7d586cf6d4d6fd9f5e488d2f7f8fe436 ns-3.22
8ea92bc090cdc01e683a2116d7668ce745ae92e2 ns-3.23
56928998e05c9c11f5f3aefe79be8d2843e0db88 release ns-3.0.1
7ac5a4b0969b255c4824c926c2b37ef450136ce9 release ns-3.0.2
+2 -178
View File
@@ -1,182 +1,6 @@
Alexander Afanasyev (alexander.afanasyev@ucla.edu)
Rohit Agarwal (mindprince@gmail.com)
Kirill Andreev (andreev@iitp.ru)
Dean Armstrong (deanarm@gmail.com)
Stefano Avallone (stefano.avallone@unina.it)
Ghada Badawy (gbadawy@gmail.com)
Nicola Baldo (nbaldo@cttc.es)
Mirko Banchi (mk.banchi@gmail.com)
Peter D. Barnes, Jr. (barnes26@llnl.gov)
Ramon Bauza (monbauza@gmail.com)
Mehdi Benamor (mehdi.benamor@telecom-bretagne.eu)
Raj Bhattacharjea (raj.b@gatech.edu)
Timo Bingmann (timo.bingmann@student.kit.edu)
Julien Boite (juboite@gmail.com)
Biljana Bojovic (bbojovic@cttc.es)
Elena Borovkova (borokovaes@iitp.ru)
Pavel Boyko (boyko@iitp.ru)
Dan Broyles (muxman@sbcglobal.net)
Jonathan Brugge (j.d.brugge@student.utwente.nl)
Junling Bu (linlinjavaer@gmail.com)
Elena Buchatskaia (borovkovaes@iitp.ru)
Nuno Cardoso (nunopcardoso@gmail.com)
Raj Bhattarcharjea (raj.b@gatech.edu)
Gustavo Carneiro (gjc@inescporto.pt, gjcarneiro@gmail.com)
Scott Carpenter (scarpen@ncsu.edu)
Tiago Cerqueira (tiago.miguel43@gmail.com)
Egemen K. Cetinkaya (ekc@iitc.ku.edu)
Angelos Chatzipapas (chatzipa@ceid.upatras.gr)
Eugene Chemeritskiy (echemeritskiy@arccn.ru)
Yufei Cheng (yfcheng@ittc.ku.edu)
Andrey Churin (aachurin@gmail.com)
Benjamin Cizdziel (ben.cizdziel@gmail.com)
Salva Climent (jocliba@gmail.com)
Luis Cortes (cortes@gatech.edu)
Luca Costantino (luca.costantino@gmail.com)
Matthieu Coudron (matthieu.coudron@lip6.fr)
Alexander D'souza (moijes12@gmail.com)
Sébastien Deronne (sebastien.deronne@gmail.com)
Craig Dowell (craigdo@ee.washington.edu)
Gilaras Drakeson (gilaras@gmail.com)
Christian Facchini (c.facchini@gmail.com)
Denis Fakhriev (fakhriev@iitp.ru)
Jahanzeb Farooq (Jahanzeb.Farooq@inria.fr, Jahanzeb.Farooq@gmail.com)
Luca Favatella (slackydeb@gmail.com)
Margherita Filippetti (morag87@gmail.com)
Pedro Fortuna (pedro.fortuna@inescporto.pt)
Juliana Freitag Borin (juliana.freitag@gmail.com)
Piotr Gawlowicz (gawlowicz.p@gmail.com)
Eric Gamess (egamess@gmail.com)
Yida Gao (yidapb@gmail.com)
Thomas Geithner (thomas.geithner@dai-labor.de)
Ashim Ghosh (ashim.atiit@gmail.com)
Martin Giachino (martin.giachino@gmail.com,giachino@fing.edu.uy)
Tom Goff (tgoff@tgoff.net)
Mohit Goyal (mohit.bits@gmail.com)
Juan C. Granda (jcgranda@uniovi.es)
David Gross (gdavid.devel@gmail.com)
Maja Grubišić (maja.grubisic@live.com)
Charline Taibi Guguen (charline.guguen@gmail.com)
Daniel Halperin (daniel@halper.in)
Bruno Haick (bghaick@hotmail.com)
Frank Helbert (frank@ime.usp.br)
Tom Henderson (tomhend@u.washington.edu)
Christopher Hepner (hepner@hs-ulm.de)
Budiarto Herman (budiarto.herman@magister.fi)
Tom Hewer (tomhewer@mac.com)
Kristian A. Hiorth (kristahi@ifi.uio.no)
Kim Højgaard-Hansen (kimrhh@gmail.com)
Chris Hood (chood8@gatech.edu)
Blake Hurd (naimorai@gmail.com)
ishan (ishan.chhabra@gmail.com)
Mohamed Amine Ismail (amine.ismail@inria.fr, iamine@udcast.com)
Jared Ivey (j.ivey@gatech.edu)
Atishay Jain (atishayjain25@gmail.com)
Sascha Alexander Jopen (jopen@informatik.uni-bonn.de)
Sam Jansen (sam.jansen@gmail.com)
Liu Jian (liujatp@gmail.com)
Piotr Jurkiewicz (piotr.jerzy.jurkiewicz@gmail.com)
Evgeny Kalishenko (ydginster@gmail.com)
Konstantinos Katsaros (dinos.katsaros@gmail.com)
Morteza Kheirkhah (m.kheirkhah@sussex.ac.uk)
Flavio Kobuta (flaviokubota@gmail.com)
Joe Kopena (tjkopena@cs.drexel.edu)
Christopher Kosecki (christopher.l.kosecki.ctr@mail.mil)
Aleksey Kovalenko (kovalenko@iitp.ru)
Mathieu Lacage (mathieu.lacage@inria.fr)
Emmanuelle Laprise (emmmanuelle.laprise@bluekazoo.ca)
Kristijan Lenković (k.lenkovic@me.com)
Daniel Lertpratchya (nikkipui@gmail.com)
Brett Levasseur (brettl20@gmail.com)
Björn Lichtblau (lichtbla@informatik.hu-berlin.de)
Timo Lindhorst (tlnd@online.de)
Erwan Livolant (erwan.livolant@inria.fr)
Keith Ma (keith.nwsuaf@gmail.com)
Federico Maguolo (maguolof@dei.unipd.it)
Antti Makela (zarhan@cc.hut.fi)
Francesco Malandrino (francesco.malandrino@gmail.com)
Rubén Martínez (rmartinez@deic.uab.cat)
Fabian Mauchle (f1mauchl@hsr.ch)
Andrey Mazo (mazo@iitp.ru)
Jonathan McCrohan (jmccroha@tcd.ie)
Andrew McGregor (andrewmcgr@gmail.com)
Vedran Miletić (rivanvx@gmail.com)
Jens Mittag (jens.mittag@kit.edu)
Marco Miozzo (mmiozzo@cttc.es)
Faker Moatamri (faker.moatamri@inria.fr)
Edvin Močibob (edvin.mocibob@gmail.com)
Mike Moreton (mjvm_ns@hotmail.com)
Michele Muccio (michelemuccio@virgilio.it)
Esteban Municio (esteban.municio@urjc.es)
Sidharth Nabar (snabar@uw.edu)
Hemanth Narra (hemanth@ittc.ku.edu)
Roman Naumann (naumann@informatik.hu-berlin.de)
Ben Newton (bn@cs.unc.edu)
Cecchi Niccolò (insa@igeek.it)
Andreas Nilsson (andrnils@gmail.com)
Jaume Nin (jnin@cttc.es)
Michael Nowatkowski (nowatkom@gmail.com)
Anh Nguyen (annguyen@ittc.ku.edu)
Duy Nguyen (duy@soe.ucsc.edu)
Luis Pacheco (luisbelem@gmail.com)
Lluís Parcerisa (parcerisa@gmail.com)
Natale Patriciello (natale.patriciello@gmail.com)
Tommaso Pecorella (tommaso.pecorella@unifi.it)
Guangyu Pei (guangyu.pei@boeing.com)
Josh Pelkey (jpelkey@gatech.edu)
Per (per_e_lists@rocketmail.com)
Fernando Pereira (ferdonfeup@gmail.com)
Colin Perkins (csp@csperkins.org)
Giuseppe Piro (g.piro@poliba.it)
Yana Podkosova (yanapdk@rambler.ru)
Vikas Pushkar (vikaskupushkar@gmail.com)
Andrea Ranieri (andreran@uno.it)
Bruno Ranieri (Yrrsinn@googlemail.com)
Ken Renard (kenneth.renard@arl.army.mil)
Manuel Requena (mrequena@cttc.es)
Matias Richart (mrichart@fing.edu.uy)
Mathieu Lacage (mathieu.lacage@sophia.inria.fr)
George F. Riley (riley@ece.gatech.edu)
Juergen Rinas (jrinas@gmx.de)
Sebastian Rohde (sebastian.rohde@tu-dortmund.de)
Karsten Roscher (sfx@rocktale.de)
Bill Roome (wdr@bell-labs.com)
David (david.rua@gmail.com)
Andrea Sacco (andrea.sacco85@gmail.com)
Lynne Salameh (l.salameh@cs.ucl.ac.uk)
Providence Salumu Munga (Providence.Salumu@gmail.com, Providence.Salumu_Munga@it-sudparis.eu)
Francisco Javier Sánchez-Roselly (fnavarro@ujaen.es)
Florian Schmidt (Florian.Schmidt@cs.rwth-aachen.de)
Guillaume Seguin (guillaume.seguin@inria.fr)
Ioannis Selinis (selinis.g@gmail.com)
Tomasz Seweryn (tomasz.seweryn7@gmail.com)
Dmitrii Shakshin (d.shakshin@gmail.com)
Kulin Shah (m.kulin@gmail.com)
Guowang Shi (shiguowang2007@gmail.com)
Phillip Sitbon (phillip.sitbon@gmail.com)
Pedro Silva (pmms@inesctec.pt)
Anirudh Sivaraman (sk.anirudh@gmail.com)
Steven Smith (smith84@llnl.gov)
Andrew Stanton (acstanton515@gmail.com)
Ewgenij Starostin (estar@cs.tu-berlin.de)
YunQiang Su (wzssyqa@gmail.com)
Brian Swenson (bswenson3@gatech.edu)
Lalith Suresh (suresh.lalith@gmail.com)
Dave Taht (dave.taht@bufferbloat.net)
Marcos Talau (talau@users.sourceforge.net)
Adrian S. W. Tam (adrian.sw.tam@gmail.com)
Cristiano Tapparello (cristiano.tapparello@rochester.edu)
Hajime Tazaki (tazaki@sfc.wide.ad.jp)
Wilson Thong (wilsonwk@ee.cityu.edu.hk)
Mauro Tortonesi (mauro.tortonesi@unife.it)
Quincy Tse (quincy.tse@gmail.com)
Frederic Urbani (frederic.urbani@inria.fr)
Andras Varga (andras@omnetpp.org)
Sebastien Vincent (vincent@clarinet.u-strasbg.fr)
Guillaume Vu-Brugier (gvubrugier@gmail.com)
Tom Wambold (tom5760@gmail.com)
Danqi Wang (beyondwdq@gmail.com)
Mitch Watrous (watrous@u.washington.edu)
Florian Westphal (fw@strlen.de)
He Wu (mdzz@u.washington.edu)
Yoshihiko Yazawa (yoshiyaz@gmail.com)
Dizhi Zhou (dizhi.zhou@gmail.com)
Gaurav Sathe (gaurav.sathe@tcs.com)
-2556
View File
File diff suppressed because it is too large Load Diff
-21
View File
@@ -1,21 +0,0 @@
# Makefile wrapper for waf
all:
./waf
# free free to change this part to suit your requirements
configure:
#./waf configure --enable-examples --enable-tests
./waf configure -d optimized --enable-examples --disable-python --disable-tests
build:
./waf build
install:
./waf install
clean:
./waf clean
distclean:
./waf distclean
+102 -55
View File
@@ -1,9 +1,3 @@
Note:
This is a custom and unsupported fork of NS-3 simulator (http://www.nsnam.org/).
**The code in this repository is frequently rebased on top of the latest ns-3-dev branch**
The Network Simulator, Version 3
--------------------------------
@@ -11,36 +5,63 @@ This is a custom and unsupported fork of NS-3 simulator (http://www.nsnam.org/).
Table of Contents:
------------------
1) An overview
2) Building ns-3
3) Running ns-3
4) Getting access to the ns-3 documentation
5) Working with the development version of ns-3
1) An Open Source project
2) An overview of the ns-3 project
3) Building ns-3
4) Running ns-3
5) Getting access to the ns-3 documentation
6) Working with the development version of ns-3
Note: Much more substantial information about ns-3 can be found at
http://www.nsnam.org
1) An Open Source project
-------------------------
ns-3 is a free open source project aiming to build a discrete-event
network simulator targeted for simulation research and education.
This is a collaborative project; we hope that
ns-3 is an Open Source project. We intend to make this
project a successful collaborative project: we hope that
the missing pieces of the models we have not yet implemented
will be contributed by the community in an open collaboration
process.
The process of contributing to the ns-3 project varies with
the people involved, the amount of time they can invest
and the type of model they want to work on, but the current
process that the project tries to follow is described here:
http://www.nsnam.org/developers/contributing-code/
Contributing to the ns-3 project is still a very informal
process because that process depends heavily on the personality
of the people involved, the amount of time they can invest
and the type of model they want to work on.
This README excerpts some details from a more extensive
tutorial that is maintained at:
http://www.nsnam.org/documentation/latest/
Despite this lack of a formal process, there are a number of
steps which naturally stem from the open-source roots of the
project. These steps are described in doc/contributing.txt
2) Building ns-3
2) An overview of the ns-3 project
----------------------------------
This package contains the latest version of ns-3 which is aims
at being a replacement for ns-2. Currently, ns-3 provides a
number of very simple network simulation models:
- an ipv4 and udp stack
- arp support at the bottom of the stack
- point-to-point physical-layer links
- OnOff traffic generator
Our focus to date has been on getting an overall software
framework in place. The framework is there to make adding
new models as simple as possible:
- an extensive tracing system can be used to connect
any number of arbitrary trace sources to any number
of trace sinks. This tracing system is decoupled
from the act of serializing the trace events to a
file: users can and should provide their own
trace handling routines.
- simple file trace serialization support is included
to both text and pcap files.
- adding new MAC-level models simply requires subclassing
the pair of classes NetDevice and Channel.
- adding new traffic generation algorithms is also very
simple through the Application and the Socket classes.
3) Building ns-3
----------------
The code for the framework and the default models provided
@@ -50,65 +71,91 @@ use of these ns-3 libraries.
To build the set of default libraries and the example
programs included in this package, you need to use the
tool 'waf'. Detailed information on how use waf is
included in the file doc/build.txt
tool 'scons'. Detailed information on how to install
and use scons is included in the file doc/build.txt
However, the real quick and dirty way to get started is to
type the command
./waf configure --enable-examples
followed by
./waf
in the the directory which contains
type the command "scons" the the directory which contains
this README file. The files built will be copied in the
build/ directory.
build-dir/dbg-shared/bin and build-dir/dbg-shared/lib
directories. build-dir/dbg-shared/bin will contain
one binary for each of the sample code in the 'samples'
directory and one binary for each of the detailed examples
found in the 'examples' directory.
The current codebase is expected to build and run on the
set of platforms listed in the RELEASE_NOTES file.
following set of platforms:
- linux x86 gcc 4.2, 4.1, and, 3.4.
- linux x86_64 gcc 4.0
- MacOS X ppc and x86
Other platforms may or may not work: we welcome patches to
improve the portability of the code to these other platforms.
The current codebase is expected to fail to build on
the following platforms:
- gcc 3.3 and earlier
- optimized builds on linux x86 gcc 4.0
3) Running ns-3
Other platforms may or may not work: we welcome
patches to improve the portability of the code to these
other platforms.
4) Running ns-3
---------------
On recent Linux systems, once you have built ns-3 (with examples
enabled), it should be easy to run the sample programs with the
following command, such as:
On recent Linux systems, once you have built ns-3, it
should be easy to run the sample programs with the
following command:
./waf --run simple-global-routing
./build-dir/dbg-shared/bin/simple-p2p
That program should generate a simple-global-routing.tr text
trace file and a set of simple-global-routing-xx-xx.pcap binary
pcap trace files, which can be read by tcpdump -tt -r filename.pcap
The program source can be found in the examples/routing directory.
or:
4) Getting access to the ns-3 documentation
cd build-dir/dbg-shared/bin
./simple-p2p
That program should generate a simple-p2p.tr text
trace file and a set of simple-p2p-xx-xx.pcap binary
pcap trace files, which can be read by tcpdump.
5) Getting access to the ns-3 documentation
-------------------------------------------
Once you have verified that your build of ns-3 works by running
the simple-point-to-point example as outlined in 4) above, it is
the simple-p2p example as outlined in 4) above, it is
quite likely that you will want to get started on reading
some ns-3 documentation.
All of that documentation should always be available from
the ns-3 website: http:://www.nsnam.org/documentation/.
the ns-3 website: http:://www.nsnam.org/ but we
include some of it in this release for ease of use.
This documentation includes:
- a tutorial
- a reference manual
- models in the ns-3 model library
- an architecture document which describes a very
high-level view of ns-3: it tries to explain the
use-cases the ns-3 developers really focused on when
doing the initial design and then goes on to explain
the structure of the resulting framework.
See the file doc/architecture.pdf
- a wiki for user-contributed tips: http://www.nsnam.org/wiki/
- API documentation generated using doxygen: this is
- an API documentation generated using doxygen: this is
a reference manual, most likely not very well suited
as introductory text:
http://www.nsnam.org/doxygen/index.html
5) Working with the development version of ns-3
If you want to re-generate this documentation, you can
easily do so:
- doc/architecture.pdf is generated from the architecture.tex
file in http://code.nsnam.org/docs
- the doxygen documentation is generated from the doc/doxygen.conf
configuration file. The command "scons doc" will generate it
as doc/html/index.html if you have installed the doxygen tools
(see http://www.doxygen.org)
6) Working with the development version of ns-3
-----------------------------------------------
If you want to download and use the development version
+4 -2444
View File
File diff suppressed because it is too large Load Diff
+475
View File
@@ -0,0 +1,475 @@
## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
import os.path
import build
version_file = open ('VERSION', 'r')
version = version_file.readline ()
version_file.close ()
version = version.strip ()
ns3 = build.Ns3()
ns3.build_dir = 'build-dir'
ns3.version = version
ns3.name = 'ns3'
ns3.distname = 'ns'
ns3.doxygen_config = os.path.join('doc', 'doxygen.conf')
ns3.add_extra_dist(os.path.join('doc', 'main.txt'))
ns3.add_extra_dist ('doc/architecture.pdf')
ns3.add_extra_dist ('doc/contributing.txt')
ns3.add_extra_dist ('doc/build.txt')
ns3.add_extra_dist ('doc/codingstd.txt')
ns3.add_extra_dist ('doc/mercurial.txt')
ns3.add_extra_dist ('README')
ns3.add_extra_dist ('RELEASE_NOTES')
ns3.add_extra_dist ('AUTHORS')
ns3.add_extra_dist ('VERSION')
ns3.add_extra_dist('doc/build-waf.txt')
ns3.add_extra_dist('ns3/_placeholder_')
for wscript in [
"src/core/wscript",
"src/node/wscript",
"src/devices/p2p/wscript",
"src/common/wscript",
"src/applications/wscript",
"src/simulator/wscript",
"src/internet-node/wscript",
"src/wscript",
"utils/wscript",
"samples/wscript",
"examples/wscript",
"wscript",
]:
ns3.add_extra_dist(wscript)
ns3.add_extra_dist('waf')
ns3.add_extra_dist('waf.bat')
#
# The Core module
#
core = build.Ns3Module('core', 'src/core')
ns3.add(core)
core.add_sources([
'callback-test.cc',
'debug.cc',
'assert.cc',
'ptr.cc',
'object.cc',
'test.cc',
'random-variable.cc',
'rng-stream.cc',
'uid-manager.cc',
'default-value.cc',
'command-line.cc',
'type-name.cc',
'component-manager.cc',
])
env = Environment()
if env['PLATFORM'] == 'posix' or env['PLATFORM'] == 'darwin' or env['PLATFORM'] == 'cygwin':
core.add_external_dep('pthread')
core.add_sources([
'unix-system-wall-clock-ms.cc',
])
elif env['PLATFORM'] == 'win32':
core.add_sources([
'win32-system-wall-clock-ms.cc',
])
core.add_headers ([
'uid-manager.h',
'singleton.h',
])
core.add_inst_headers([
'system-wall-clock-ms.h',
'empty.h',
'callback.h',
'ptr.h',
'object.h',
'debug.h',
'assert.h',
'fatal-error.h',
'test.h',
'random-variable.h',
'rng-stream.h',
'default-value.h',
'command-line.h',
'type-name.h',
'component-manager.h',
])
def config_core (env, config):
retval = []
# XXX This check is primitive but it should be
# good enough for now.
if config.CheckCHeader ('stdlib.h') == 1:
retval.append ('#define HAVE_STDLIB_H 1')
retval.append ('#define HAVE_GETENV 1')
else:
retval.append ('#undef HAVE_STDLIB_H')
retval.append ('#undef HAVE_GETENV')
return retval
core.add_config (config_core)
#
# The Simu module
#
simu = build.Ns3Module('simulator', 'src/simulator')
ns3.add(simu)
simu.add_dep('core')
simu.add_external_dep('m')
simu.add_sources([
'high-precision.cc',
'time.cc',
'event-id.cc',
'scheduler.cc',
'scheduler-factory.cc',
'scheduler-list.cc',
'scheduler-heap.cc',
'scheduler-map.cc',
'event-impl.cc',
'simulator.cc',
])
simu.add_headers([
'scheduler-heap.h',
'scheduler-map.h',
'scheduler-list.h'
])
simu.add_inst_headers([
'high-precision.h',
'nstime.h',
'event-id.h',
'event-impl.h',
'simulator.h',
'scheduler.h',
'scheduler-factory.h',
'simulation-singleton.h',
])
high_precision_as_double = ARGUMENTS.get('high-precision-as-double', 'n')
if high_precision_as_double == 'y':
simu.add_inst_header ('high-precision-double.h')
simu.add_source ('high-precision-double.cc')
else:
simu.add_inst_headers ([
'high-precision-128.h',
'cairo-wideint-private.h'
])
simu.add_sources ([
'high-precision-128.cc',
'cairo-wideint.c',
])
def config_simulator (env, config):
retval = []
high_precision_as_double = ARGUMENTS.get('high-precision-as-double', 'n')
if high_precision_as_double == 'y':
retval.append ('#define USE_HIGH_PRECISION_DOUBLE 1')
else:
retval.append ('#undef USE_HIGH_PRECISION_DOUBLE')
if config.CheckCHeader ('stdint.h') == 1:
retval.append ('#define HAVE_STDINT_H 1')
elif config.CheckCHeader ('inttypes.h') == 1:
retval.append ('#define HAVE_INTTYPES_H 1')
elif config.CheckCHeader ('sys/inttypes.h') == 1:
retval.append ('#define HAVE_SYS_INT_TYPES_H 1')
return retval
simu.add_config (config_simulator)
#
# The Common module
#
common = build.Ns3Module('common', 'src/common')
common.add_deps(['core', 'simulator'])
ns3.add(common)
common.add_sources([
'buffer.cc',
'chunk.cc',
'header.cc',
'trailer.cc',
'packet.cc',
'tags.cc',
'pcap-writer.cc',
'variable-tracer-test.cc',
'trace-context.cc',
'trace-resolver.cc',
'callback-trace-source.cc',
'empty-trace-resolver.cc',
'composite-trace-resolver.cc',
'trace-root.cc',
'data-rate.cc',
])
common.add_headers ([
])
common.add_inst_headers([
'buffer.h',
'chunk.h',
'header.h',
'trailer.h',
'tags.h',
'packet.h',
'uv-trace-source.h',
'sv-trace-source.h',
'fv-trace-source.h',
'pcap-writer.h',
'callback-trace-source.h',
'trace-context.h',
'trace-resolver.h',
'empty-trace-resolver.h',
'composite-trace-resolver.h',
'array-trace-resolver.h',
'trace-root.h',
'terminal-trace-resolver.h',
'data-rate.h',
])
node = build.Ns3Module ('node', 'src/node')
ns3.add (node)
node.add_deps (['core', 'common', 'simulator'])
node.add_sources ([
'node.cc',
'ipv4-address.cc',
'net-device.cc',
'mac-address.cc',
'llc-snap-header.cc',
'ipv4-route.cc',
'queue.cc',
'drop-tail-queue.cc',
'channel.cc',
'node-list.cc',
'socket.cc',
'socket-factory.cc',
'udp.cc',
'ipv4.cc',
'application.cc',
])
node.add_inst_headers ([
'node.h',
'ipv4-address.h',
'net-device.h',
'mac-address.h',
'ipv4-route.h',
'queue.h',
'drop-tail-queue.h',
'llc-snap-header.h',
'channel.h',
'node-list.h',
'socket.h',
'socket-factory.h',
'udp.h',
'ipv4.h',
'application.h',
])
applications = build.Ns3Module ('applications', 'src/applications')
ns3.add (applications)
applications.add_deps (['node'])
applications.add_sources ([
'onoff-application.cc',
])
applications.add_inst_headers ([
'onoff-application.h',
])
inode = build.Ns3Module ('internet-node', 'src/internet-node')
ns3.add (inode)
inode.add_deps (['node'])
inode.add_sources ([
'internet-node.cc',
'l3-demux.cc',
'l3-protocol.cc',
'ipv4-l4-demux.cc',
'ipv4-l4-protocol.cc',
'ipv4-header.cc',
'udp-header.cc',
'ipv4-checksum.cc',
'ipv4-interface.cc',
'ipv4-l3-protocol.cc',
'ipv4-end-point.cc',
'udp-l4-protocol.cc',
'arp-header.cc',
'arp-cache.cc',
'arp-ipv4-interface.cc',
'arp-l3-protocol.cc',
'ipv4-loopback-interface.cc',
'header-utils.cc',
'udp-socket.cc',
'ipv4-end-point-demux.cc',
'arp-private.cc',
'ipv4-impl.cc',
'ipv4-private.cc',
'ascii-trace.cc',
'pcap-trace.cc',
'udp-impl.cc',
])
inode.add_headers ([
'ipv4-header.h',
'udp-header.h',
'ipv4-checksum.h',
'arp-header.h',
'arp-cache.h',
'arp-l3-protocol.h',
'ipv4-loopback-interface.h',
'l3-demux.h',
'header-utils.h',
'arp-ipv4-interface.h',
'udp-socket.h',
'udp-l4-protocol.h',
'arp-private.h',
'ipv4-impl.h',
'ipv4-private.h',
'ipv4-l3-protocol.h',
'l3-protocol.h',
'ipv4-l4-protocol.h',
'ipv4-l4-demux.h',
'ipv4-end-point-demux.h',
'ipv4-end-point.h',
'ipv4-header.h',
'udp-header.h',
'ipv4-interface.h',
'sgi-hashmap.h',
'udp-impl.h',
])
inode.add_inst_headers ([
'internet-node.h',
'ascii-trace.h',
'pcap-trace.h',
])
p2p = build.Ns3Module ('p2p', 'src/devices/p2p')
ns3.add (p2p)
p2p.add_deps (['node'])
p2p.add_sources ([
'p2p-net-device.cc',
'p2p-channel.cc',
'p2p-topology.cc',
])
p2p.add_inst_headers ([
'p2p-net-device.h',
'p2p-channel.h',
'p2p-topology.h',
])
# utils
run_tests = build.Ns3Module('run-tests', 'utils')
ns3.add(run_tests)
run_tests.set_executable()
run_tests.add_deps(['core', 'simulator', 'common'])
run_tests.add_source('run-tests.cc')
bench_object = build.Ns3Module('bench-object', 'utils')
ns3.add(bench_object)
bench_object.set_executable()
bench_object.add_deps(['core'])
bench_object.add_source('bench-object.cc')
bench_packets = build.Ns3Module('bench-packets', 'utils')
#ns3.add(bench_packets)
bench_packets.set_executable()
bench_packets.add_dep('core')
bench_packets.add_source('bench-packets.cc')
bench_simu = build.Ns3Module('bench-simulator', 'utils')
ns3.add(bench_simu)
bench_simu.set_executable()
bench_simu.add_dep('simulator')
bench_simu.add_source('bench-simulator.cc')
replay_simu = build.Ns3Module('replay-simulation', 'utils')
ns3.add(replay_simu)
replay_simu.set_executable()
replay_simu.add_dep('simulator')
replay_simu.add_source('replay-simulation.cc')
# samples
sample_debug = build.Ns3Module('sample-debug', 'samples')
sample_debug.set_executable()
ns3.add(sample_debug)
sample_debug.add_dep('core')
sample_debug.add_source('main-debug.cc')
sample_debug.add_source('main-debug-other.cc')
sample_callback = build.Ns3Module('sample-callback', 'samples')
sample_callback.set_executable()
ns3.add(sample_callback)
sample_callback.add_dep('core')
sample_callback.add_source('main-callback.cc')
sample_ptr = build.Ns3Module('sample-ptr', 'samples')
sample_ptr.set_executable()
ns3.add(sample_ptr)
sample_ptr.add_dep('core')
sample_ptr.add_source('main-ptr.cc')
sample_trace = build.Ns3Module('sample-trace', 'samples')
#ns3.add(sample_trace)
sample_trace.add_dep('common')
sample_trace.set_executable()
sample_trace.add_source('main-trace.cc')
sample_query_interface = build.Ns3Module('sample-query-interface', 'samples')
ns3.add(sample_query_interface)
sample_query_interface.add_dep('common')
sample_query_interface.set_executable()
sample_query_interface.add_source('main-query-interface.cc')
sample_simu = build.Ns3Module('sample-simulator', 'samples')
ns3.add(sample_simu)
sample_simu.set_executable()
sample_simu.add_dep('simulator')
sample_simu.add_source('main-simulator.cc')
sample_packet = build.Ns3Module('sample-packet', 'samples')
ns3.add(sample_packet)
sample_packet.set_executable()
sample_packet.add_dep('common')
sample_packet.add_source('main-packet.cc')
sample_test = build.Ns3Module('sample-test', 'samples')
sample_test.set_executable()
ns3.add(sample_test)
sample_test.add_dep('core')
sample_test.add_source('main-test.cc')
sample_simple = build.Ns3Module('sample-simple', 'samples')
sample_simple.set_executable()
ns3.add(sample_simple)
sample_simple.add_deps(['core', 'simulator', 'node', 'internet-node'])
sample_simple.add_source('main-simple.cc')
sample_sp2p = build.Ns3Module('sample-simple-p2p', 'samples')
sample_sp2p.set_executable()
#n3.add(sample_sp2p)
sample_sp2p.add_deps(['core', 'simulator', 'node', 'internet-node', 'p2p'])
sample_sp2p.add_source('main-simple-p2p.cc')
sample_default_value = build.Ns3Module('sample-default-value', 'samples')
sample_default_value.set_executable()
ns3.add(sample_default_value)
sample_default_value.add_deps(['core', 'simulator', 'node', 'p2p'])
sample_default_value.add_source('main-default-value.cc')
sample_object = build.Ns3Module('sample-object', 'samples')
sample_object.set_executable()
ns3.add(sample_object)
sample_object.add_deps(['core'])
sample_object.add_source('main-object.cc')
sample_component_manager = build.Ns3Module('sample-component-manager', 'samples')
sample_component_manager.set_executable()
ns3.add(sample_component_manager)
sample_component_manager.add_deps(['core'])
sample_component_manager.add_source('main-component-manager.cc')
# examples
example_simple_p2p = build.Ns3Module('simple-p2p', 'examples')
example_simple_p2p.set_executable()
ns3.add(example_simple_p2p)
example_simple_p2p.add_deps(['core', 'simulator', 'node', 'p2p', 'internet-node', 'applications'])
example_simple_p2p.add_source('simple-p2p.cc')
ns3.generate_dependencies()
+1 -1
View File
@@ -1 +1 @@
3-dev
3.0.3
@@ -1,13 +0,0 @@
from pybindgen import Module, FileCodeSink, write_preamble, param, retval
def register_types(module):
module.add_class('MyClass')
def register_methods(root_module):
MyClass = root_module['MyClass']
MyClass.add_constructor([], visibility='public')
MyClass.add_constructor([param('double', 's'), param('double', 'l'), param('double', 'mean')], visibility='public')
def register_functions(module):
module.add_function('SomeFunction', 'int', [param('int', 'xpto')])
View File
View File
-7
View File
@@ -1,7 +0,0 @@
from _ns3 import *
import atexit
atexit.register(Simulator.Destroy)
del atexit
-336
View File
@@ -1,336 +0,0 @@
#include "ns3module.h"
#include "ns3/ref-count-base.h"
namespace ns3{
void PythonCompleteConstruct (Ptr<Object> object, TypeId typeId, const AttributeList &attributes)
{
object->SetTypeId (typeId);
object->Object::Construct (attributes);
}
}
class PythonEventImpl : public ns3::EventImpl
{
private:
PyObject *m_callback;
PyObject *m_args;
public:
PythonEventImpl (PyObject *callback, PyObject *args)
{
m_callback = callback;
Py_INCREF(m_callback);
m_args = args;
Py_INCREF(m_args);
}
virtual ~PythonEventImpl ()
{
PyGILState_STATE __py_gil_state;
__py_gil_state = (PyEval_ThreadsInitialized() ? PyGILState_Ensure() : (PyGILState_STATE) 0);
Py_DECREF(m_callback);
Py_DECREF(m_args);
if (PyEval_ThreadsInitialized())
PyGILState_Release(__py_gil_state);
}
virtual void Notify ()
{
PyGILState_STATE __py_gil_state;
__py_gil_state = (PyEval_ThreadsInitialized() ? PyGILState_Ensure() : (PyGILState_STATE) 0);
PyObject *retval = PyObject_CallObject(m_callback, m_args);
if (retval) {
if (retval != Py_None) {
PyErr_SetString(PyExc_TypeError, "event callback should return None");
PyErr_Print();
}
Py_DECREF(retval);
} else {
PyErr_Print();
}
if (PyEval_ThreadsInitialized())
PyGILState_Release(__py_gil_state);
}
};
PyObject *
_wrap_Simulator_Schedule(PyNs3Simulator *PYBINDGEN_UNUSED(dummy), PyObject *args, PyObject *kwargs,
PyObject **return_exception)
{
PyObject *exc_type, *traceback;
PyObject *py_time;
PyObject *py_callback;
PyObject *user_args;
ns3::Ptr<PythonEventImpl> py_event_impl;
PyNs3EventId *py_EventId;
if (kwargs && PyObject_Length(kwargs) > 0) {
PyErr_SetString(PyExc_TypeError, "keyword arguments not supported");
goto error;
}
if (PyTuple_GET_SIZE(args) < 2) {
PyErr_SetString(PyExc_TypeError, "ns3.Simulator.Schedule needs at least 2 arguments");
goto error;
}
py_time = PyTuple_GET_ITEM(args, 0);
py_callback = PyTuple_GET_ITEM(args, 1);
if (!PyObject_IsInstance(py_time, (PyObject*) &PyNs3Time_Type)) {
PyErr_SetString(PyExc_TypeError, "Parameter 1 should be a ns3.Time instance");
goto error;
}
if (!PyCallable_Check(py_callback)) {
PyErr_SetString(PyExc_TypeError, "Parameter 2 should be callable");
goto error;
}
user_args = PyTuple_GetSlice(args, 2, PyTuple_GET_SIZE(args));
py_event_impl = ns3::Create<PythonEventImpl>(py_callback, user_args);
Py_DECREF(user_args);
py_EventId = PyObject_New(PyNs3EventId, &PyNs3EventId_Type);
py_EventId->obj = new ns3::EventId(
ns3::Simulator::Schedule(*((PyNs3Time *) py_time)->obj, py_event_impl));
return (PyObject *) py_EventId;
error:
PyErr_Fetch(&exc_type, return_exception, &traceback);
Py_XDECREF(exc_type);
Py_XDECREF(traceback);
return NULL;
}
PyObject *
_wrap_Simulator_ScheduleNow(PyNs3Simulator *PYBINDGEN_UNUSED(dummy), PyObject *args, PyObject *kwargs,
PyObject **return_exception)
{
PyObject *exc_type, *traceback;
PyObject *py_callback;
PyObject *user_args;
ns3::Ptr<PythonEventImpl> py_event_impl;
PyNs3EventId *py_EventId;
if (kwargs && PyObject_Length(kwargs) > 0) {
PyErr_SetString(PyExc_TypeError, "keyword arguments not supported");
goto error;
}
if (PyTuple_GET_SIZE(args) < 1) {
PyErr_SetString(PyExc_TypeError, "ns3.Simulator.Schedule needs at least 1 argument");
goto error;
}
py_callback = PyTuple_GET_ITEM(args, 0);
if (!PyCallable_Check(py_callback)) {
PyErr_SetString(PyExc_TypeError, "Parameter 2 should be callable");
goto error;
}
user_args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
py_event_impl = ns3::Create<PythonEventImpl>(py_callback, user_args);
Py_DECREF(user_args);
py_EventId = PyObject_New(PyNs3EventId, &PyNs3EventId_Type);
py_EventId->obj = new ns3::EventId(ns3::Simulator::ScheduleNow(py_event_impl));
return (PyObject *) py_EventId;
error:
PyErr_Fetch(&exc_type, return_exception, &traceback);
Py_XDECREF(exc_type);
Py_XDECREF(traceback);
return NULL;
}
PyObject *
_wrap_Simulator_ScheduleDestroy(PyNs3Simulator *PYBINDGEN_UNUSED(dummy), PyObject *args, PyObject *kwargs,
PyObject **return_exception)
{
PyObject *exc_type, *traceback;
PyObject *py_callback;
PyObject *user_args;
ns3::Ptr<PythonEventImpl> py_event_impl;
PyNs3EventId *py_EventId;
if (kwargs && PyObject_Length(kwargs) > 0) {
PyErr_SetString(PyExc_TypeError, "keyword arguments not supported");
goto error;
}
if (PyTuple_GET_SIZE(args) < 1) {
PyErr_SetString(PyExc_TypeError, "ns3.Simulator.Schedule needs at least 1 argument");
goto error;
}
py_callback = PyTuple_GET_ITEM(args, 0);
if (!PyCallable_Check(py_callback)) {
PyErr_SetString(PyExc_TypeError, "Parameter 2 should be callable");
goto error;
}
user_args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
py_event_impl = ns3::Create<PythonEventImpl>(py_callback, user_args);
Py_DECREF(user_args);
py_EventId = PyObject_New(PyNs3EventId, &PyNs3EventId_Type);
py_EventId->obj = new ns3::EventId(ns3::Simulator::ScheduleDestroy(py_event_impl));
return (PyObject *) py_EventId;
error:
PyErr_Fetch(&exc_type, return_exception, &traceback);
Py_XDECREF(exc_type);
Py_XDECREF(traceback);
return NULL;
}
PyObject *
_wrap_TypeId_LookupByNameFailSafe(PyNs3TypeId *PYBINDGEN_UNUSED(dummy), PyObject *args, PyObject *kwargs,
PyObject **return_exception)
{
bool ok;
const char *name;
Py_ssize_t name_len;
ns3::TypeId tid;
PyNs3TypeId *py_tid;
const char *keywords[] = {"name", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "s#", (char **) keywords, &name, &name_len)) {
PyObject *exc_type, *traceback;
PyErr_Fetch(&exc_type, return_exception, &traceback);
Py_XDECREF(exc_type);
Py_XDECREF(traceback);
return NULL;
}
ok = ns3::TypeId::LookupByNameFailSafe(std::string(name, name_len), &tid);
if (!ok)
{
PyErr_Format(PyExc_KeyError, "The ns3 type with name `%s' is not registered", name);
return NULL;
}
py_tid = PyObject_New(PyNs3TypeId, &PyNs3TypeId_Type);
py_tid->obj = new ns3::TypeId (tid);
PyNs3TypeId_wrapper_registry[(void *) py_tid->obj] = (PyObject *) py_tid;
return (PyObject *) py_tid;
}
class CommandLinePythonValueSetter : public ns3::RefCountBase
{
PyObject *m_namespace;
std::string m_variable;
public:
CommandLinePythonValueSetter (PyObject *ns, std::string const &variable) {
Py_INCREF(ns);
m_namespace = ns;
m_variable = variable;
}
bool Parse (std::string value) {
PyObject *pyvalue = PyString_FromStringAndSize (value.data(), value.size());
PyObject_SetAttrString (m_namespace, (char *) m_variable.c_str(), pyvalue);
if (PyErr_Occurred()) {
PyErr_Print();
return false;
}
return true;
}
virtual ~CommandLinePythonValueSetter () {
Py_DECREF (m_namespace);
m_namespace = NULL;
}
};
PyObject *
_wrap_CommandLine_AddValue(PyNs3CommandLine *self, PyObject *args, PyObject *kwargs,
PyObject **return_exception)
{
const char *name, *help, *variable = NULL;
PyObject *py_namespace = NULL;
const char *keywords[] = {"name", "help", "variable", "namespace", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "ss|sO", (char **) keywords, &name, &help, &variable, &py_namespace)) {
PyObject *exc_type, *traceback;
PyErr_Fetch(&exc_type, return_exception, &traceback);
Py_XDECREF(exc_type);
Py_XDECREF(traceback);
return NULL;
}
if (variable == NULL) {
variable = name;
}
if (py_namespace == NULL) {
py_namespace = (PyObject *) self;
}
ns3::Ptr<CommandLinePythonValueSetter> setter = ns3::Create<CommandLinePythonValueSetter> (py_namespace, variable);
self->obj->AddValue (name, help, ns3::MakeCallback (&CommandLinePythonValueSetter::Parse, setter));
Py_INCREF(Py_None);
return Py_None;
}
PyObject *
_wrap_Simulator_Run(PyNs3Simulator *PYBINDGEN_UNUSED(dummy), PyObject *args, PyObject *kwargs,
PyObject **return_exception)
{
const char *keywords[] = {"signal_check_frequency", NULL};
int signal_check_frequency;
ns3::Ptr<ns3::DefaultSimulatorImpl> defaultSim =
ns3::DynamicCast<ns3::DefaultSimulatorImpl> (ns3::Simulator::GetImplementation ());
if (defaultSim) {
signal_check_frequency = 100;
} else {
signal_check_frequency = -1;
}
if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "|i", (char **) keywords, &signal_check_frequency)) {
PyObject *exc_type, *traceback;
PyErr_Fetch(&exc_type, return_exception, &traceback);
Py_XDECREF(exc_type);
Py_XDECREF(traceback);
return NULL;
}
PyThreadState *py_thread_state = NULL;
if (signal_check_frequency == -1)
{
if (PyEval_ThreadsInitialized ())
py_thread_state = PyEval_SaveThread();
ns3::Simulator::Run();
if (py_thread_state)
PyEval_RestoreThread(py_thread_state);
} else {
while (!ns3::Simulator::IsFinished())
{
if (PyEval_ThreadsInitialized())
py_thread_state = PyEval_SaveThread();
for (int n = signal_check_frequency; n > 0 && !ns3::Simulator::IsFinished(); --n)
{
ns3::Simulator::RunOneEvent();
}
if (py_thread_state)
PyEval_RestoreThread(py_thread_state);
PyErr_CheckSignals();
if (PyErr_Occurred())
return NULL;
}
}
Py_INCREF(Py_None);
return Py_None;
}
-131
View File
@@ -1,131 +0,0 @@
from __future__ import print_function
import warnings
import sys
import os
import pybindgen.settings
from pybindgen import Module, FileCodeSink, param, retval, cppclass, typehandlers
from pybindgen.module import MultiSectionFactory
import ns3modulegen_core_customizations
import logging
pybindgen.settings.wrapper_registry = pybindgen.settings.StdMapWrapperRegistry
import traceback
class ErrorHandler(pybindgen.settings.ErrorHandler):
def __init__(self, apidefs_file):
self.apidefs_file = apidefs_file
def handle_error(self, wrapper, exception, traceback_):
stack = getattr(wrapper, 'stack_where_defined', [])
stack.reverse()
for l in stack:
if l[0] == self.apidefs_file:
warnings.warn_explicit("exception %r in wrapper %s" % (exception, wrapper),
Warning, l[0], l[1])
break
else:
warnings.warn("exception %r in wrapper %s" % (exception, wrapper))
return True
#print >> sys.stderr, ">>>>>>>>>>>>>>>>>>>>>>>>>>>> ", bool(eval(os.environ["GCC_RTTI_ABI_COMPLETE"]))
pybindgen.settings.gcc_rtti_abi_complete = bool(eval(os.environ["GCC_RTTI_ABI_COMPLETE"]))
class MyMultiSectionFactory(MultiSectionFactory):
def __init__(self, main_file_name):
super(MyMultiSectionFactory, self).__init__()
self.main_file_name = main_file_name
self.main_sink = FileCodeSink(open(main_file_name, "wt"))
self.header_name = "ns3module.h"
header_file_name = os.path.join(os.path.dirname(self.main_file_name), self.header_name)
#print >> sys.stderr, ">>>>>>>>>>>>>>>>>", header_file_name, main_file_name
self.header_sink = FileCodeSink(open(header_file_name, "wt"))
def get_section_code_sink(self, section_name):
return self.main_sink
def get_main_code_sink(self):
return self.main_sink
def get_common_header_code_sink(self):
return self.header_sink
def get_common_header_include(self):
return '"%s"' % self.header_name
def close(self):
self.header_sink.file.close()
self.main_sink.file.close()
def main(argv):
logging.basicConfig()
logging.getLogger("pybindgen.typehandlers").setLevel(logging.DEBUG)
module_abs_src_path, target, extension_name, output_cc_file_name = argv[1:]
module_name = os.path.basename(module_abs_src_path)
out = MyMultiSectionFactory(output_cc_file_name)
sys.path.insert(0, os.path.join(module_abs_src_path, "bindings"))
try:
module_apidefs = __import__("modulegen__%s" % target)
del sys.modules["modulegen__%s" % target]
try:
module_customization = __import__("modulegen_customizations")
del sys.modules["modulegen_customizations"]
except ImportError:
module_customization = object()
try:
from callbacks_list import callback_classes
except ImportError as ex:
print("***************", repr(ex), file=sys.stderr)
callback_classes = []
else:
print(">>>>>>>>>>>>>>>>", repr(callback_classes), file=sys.stderr)
finally:
sys.path.pop(0)
apidefs_file, dummy = os.path.splitext(module_apidefs.__file__)
apidefs_file += '.py'
pybindgen.settings.error_handler = ErrorHandler(apidefs_file)
root_module = module_apidefs.module_init()
root_module.set_name(extension_name)
root_module.add_include('"ns3/%s-module.h"' % module_name)
ns3modulegen_core_customizations.add_std_ios_openmode(root_module)
# -----------
module_apidefs.register_types(root_module)
if hasattr(module_customization, 'post_register_types'):
module_customization.post_register_types(root_module)
# register Callback<...> type handlers
ns3modulegen_core_customizations.register_callback_classes(root_module.after_forward_declarations,
callback_classes)
# -----------
module_apidefs.register_methods(root_module)
if hasattr(module_customization, 'post_register_methods'):
module_customization.post_register_methods(root_module)
ns3modulegen_core_customizations.Object_customizations(root_module)
ns3modulegen_core_customizations.Attribute_customizations(root_module)
ns3modulegen_core_customizations.generate_callback_classes(root_module,
callback_classes)
# -----------
module_apidefs.register_functions(root_module)
if hasattr(module_customization, 'post_register_functions'):
module_customization.post_register_functions(root_module)
# -----------
root_module.generate(out)
if __name__ == '__main__':
import sys
main(sys.argv)
-177
View File
@@ -1,177 +0,0 @@
LOCAL_MODULES = [
#'my_extra_api_definitions',
]
import sys
import os
sys.path.insert(0, sys.argv[2])
from pybindgen import FileCodeSink, write_preamble
from pybindgen.module import MultiSectionFactory
import pybindgen.settings
pybindgen.settings.deprecated_virtuals = False
from ns3modulegen_generated import module_init, register_types, register_methods, register_functions
import ns3modulegen_core_customizations
import callbacks_list
import traceback
this_script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
class ErrorHandler(pybindgen.settings.ErrorHandler):
def handle_error(self, wrapper, exception, traceback_):
print >> sys.stderr
print >> sys.stderr, "---- location:"
traceback.print_stack()
print >> sys.stderr, "---- error:"
traceback.print_tb(traceback_)
try:
stack = wrapper.stack_where_defined
except AttributeError:
print >> sys.stderr, "??:??: %s / %r" % (wrapper, exception)
else:
stack = list(stack)
stack.reverse()
for (filename, line_number, function_name, text) in stack:
file_dir = os.path.dirname(os.path.abspath(filename))
if file_dir.startswith(this_script_dir):
print >> sys.stderr, "%s:%i: %r" % (os.path.join("..", "bindings", "python", os.path.basename(filename)),
line_number, exception)
break
return True
pybindgen.settings.error_handler = ErrorHandler()
pybindgen.settings.wrapper_registry = pybindgen.settings.StdMapWrapperRegistry
class MyMultiSectionFactory(MultiSectionFactory):
def __init__(self, main_file_name, modules):
super(MyMultiSectionFactory, self).__init__()
self.main_file_name = main_file_name
self.main_sink = FileCodeSink(open(main_file_name, "wt"))
self.header_name = "ns3module.h"
header_file_name = os.path.join(os.path.dirname(self.main_file_name), 'pch', self.header_name)
self.header_sink = FileCodeSink(open(header_file_name, "wt"))
self.section_sinks = {'__main__': self.main_sink}
for module in modules:
section_name = 'ns3_module_%s' % module.replace('-', '_')
file_name = os.path.join(os.path.dirname(self.main_file_name), "%s.cc" % section_name)
sink = FileCodeSink(open(file_name, "wt"))
self.section_sinks[section_name] = sink
def get_section_code_sink(self, section_name):
return self.section_sinks[section_name]
def get_main_code_sink(self):
return self.main_sink
def get_common_header_code_sink(self):
return self.header_sink
def get_common_header_include(self):
return '"%s"' % self.header_name
def close(self):
self.header_sink.file.close()
self.main_sink.file.close()
for sink in self.section_sinks.itervalues():
sink.file.close()
def main():
out = MyMultiSectionFactory(sys.argv[1], sys.argv[3:])
root_module = module_init()
root_module.add_include('"everything.h"')
register_types(root_module)
ns3modulegen_core_customizations.Simulator_customizations(root_module)
ns3modulegen_core_customizations.CommandLine_customizations(root_module)
ns3modulegen_core_customizations.TypeId_customizations(root_module)
ns3modulegen_core_customizations.add_std_ofstream(root_module)
ns3modulegen_core_customizations.add_ipv4_address_tp_hash(root_module)
for local_module in LOCAL_MODULES:
mod = __import__(local_module)
mod.register_types(root_module)
ns3modulegen_core_customizations.generate_callback_classes(root_module.after_forward_declarations,
callbacks_list.callback_classes)
register_methods(root_module)
for local_module in LOCAL_MODULES:
mod = __import__(local_module)
mod.register_methods(root_module)
ns3modulegen_core_customizations.Object_customizations(root_module)
ns3modulegen_core_customizations.Attribute_customizations(root_module)
register_functions(root_module)
for local_module in LOCAL_MODULES:
mod = __import__(local_module)
mod.register_functions(root_module)
enabled_features = os.environ['NS3_ENABLED_FEATURES'].split(',')
# if GtkConfigStore support is disabled, disable the class wrapper
if 'GtkConfigStore' not in enabled_features:
try:
root_module.classes.remove(root_module['ns3::GtkConfigStore'])
except KeyError:
pass
# if no sqlite, the class SqliteDataOutput is disabled
if 'SqliteDataOutput' not in enabled_features:
try:
root_module.classes.remove(root_module['ns3::SqliteDataOutput'])
except KeyError:
pass
if 'Threading' not in enabled_features:
for clsname in ['SystemThread', 'SystemMutex', 'SystemCondition', 'CriticalSection',
'SimpleRefCount< ns3::SystemThread, ns3::empty, ns3::DefaultDeleter<ns3::SystemThread> >']:
root_module.classes.remove(root_module['ns3::%s' % clsname])
if 'EmuNetDevice' not in enabled_features:
for clsname in ['EmuNetDevice', 'EmuHelper']:
root_module.classes.remove(root_module['ns3::%s' % clsname])
root_module.enums.remove(root_module['ns3::EmuNetDevice::EncapsulationMode'])
if 'RealTime' not in enabled_features:
for clsname in ['WallClockSynchronizer', 'RealtimeSimulatorImpl']:
root_module.classes.remove(root_module['ns3::%s' % clsname])
root_module.enums.remove(root_module['ns3::RealtimeSimulatorImpl::SynchronizationMode'])
if 'TapBridge' not in enabled_features:
for clsname in ['TapBridge', 'TapBridgeHelper', 'TapBridgeFdReader']:
root_module.classes.remove(root_module['ns3::%s' % clsname])
root_module.enums.remove(root_module['ns3::TapBridge::Mode'])
root_module.generate(out, '_ns3')
out.close()
if __name__ == '__main__':
if 0:
try:
import cProfile as profile
except ImportError:
main()
else:
print >> sys.stderr, "** running under profiler"
profile.run('main()', 'ns3modulegen.pstat')
else:
main()
@@ -1,436 +0,0 @@
from __future__ import print_function
import sys
import re
from pybindgen.typehandlers import base as typehandlers
from pybindgen import ReturnValue, Parameter
from pybindgen.cppmethod import CustomCppMethodWrapper, CustomCppConstructorWrapper
from pybindgen.typehandlers.codesink import MemoryCodeSink
from pybindgen.typehandlers import ctypeparser
from pybindgen.typehandlers.base import ForwardWrapperBase
from pybindgen import cppclass
import warnings
from pybindgen.typehandlers.base import CodeGenerationError
import sys
class SmartPointerTransformation(typehandlers.TypeTransformation):
"""
This class provides a "type transformation" that tends to support
NS-3 smart pointers. Parameters such as "Ptr<Foo> foo" are
transformed into something like Parameter.new("Foo*", "foo",
transfer_ownership=False). Return values such as Ptr<Foo> are
transformed into ReturnValue.new("Foo*",
caller_owns_return=False). Since the underlying objects have
reference counting, PyBindGen does the right thing.
"""
def __init__(self):
super(SmartPointerTransformation, self).__init__()
self.rx = re.compile(r'(ns3::|::ns3::|)Ptr<([^>]+)>\s*$')
print("{0!r}".format(self), file=sys.stderr)
def _get_untransformed_type_traits(self, name):
m = self.rx.match(name)
is_const = False
if m is None:
print("{0!r} did not match".format(name), file=sys.stderr)
return None, False
else:
name1 = m.group(2).strip()
if name1.startswith('const '):
name1 = name1[len('const '):]
is_const = True
if name1.endswith(' const'):
name1 = name1[:-len(' const')]
is_const = True
new_name = name1+' *'
if new_name.startswith('::'):
new_name = new_name[2:]
return new_name, is_const
def get_untransformed_name(self, name):
new_name, dummy_is_const = self._get_untransformed_type_traits(name)
return new_name
def create_type_handler(self, type_handler, *args, **kwargs):
if issubclass(type_handler, Parameter):
kwargs['transfer_ownership'] = False
elif issubclass(type_handler, ReturnValue):
kwargs['caller_owns_return'] = False
else:
raise AssertionError
## fix the ctype, add ns3:: namespace
orig_ctype, is_const = self._get_untransformed_type_traits(args[0])
if is_const:
correct_ctype = 'ns3::Ptr< {0} const >'.format(orig_ctype[:-2])
else:
correct_ctype = 'ns3::Ptr< {0} >'.format(orig_ctype[:-2])
args = tuple([correct_ctype] + list(args[1:]))
handler = type_handler(*args, **kwargs)
handler.set_tranformation(self, orig_ctype)
return handler
def untransform(self, type_handler, declarations, code_block, expression):
return 'const_cast<%s> (ns3::PeekPointer (%s))' % (type_handler.untransformed_ctype, expression)
def transform(self, type_handler, declarations, code_block, expression):
assert type_handler.untransformed_ctype[-1] == '*'
return 'ns3::Ptr< %s > (%s)' % (type_handler.untransformed_ctype[:-1], expression)
## register the type transformation
transf = SmartPointerTransformation()
typehandlers.return_type_matcher.register_transformation(transf)
typehandlers.param_type_matcher.register_transformation(transf)
del transf
class CallbackImplProxyMethod(typehandlers.ReverseWrapperBase):
"""
Class that generates a proxy virtual method that calls a similarly named python method.
"""
def __init__(self, return_value, parameters):
super(CallbackImplProxyMethod, self).__init__(return_value, parameters)
def generate_python_call(self):
"""code to call the python method"""
build_params = self.build_params.get_parameters(force_tuple_creation=True)
if build_params[0][0] == '"':
build_params[0] = '(char *) ' + build_params[0]
args = self.before_call.declare_variable('PyObject*', 'args')
self.before_call.write_code('%s = Py_BuildValue(%s);'
% (args, ', '.join(build_params)))
self.before_call.add_cleanup_code('Py_DECREF(%s);' % args)
self.before_call.write_code('py_retval = PyObject_CallObject(m_callback, %s);' % args)
self.before_call.write_error_check('py_retval == NULL')
self.before_call.add_cleanup_code('Py_DECREF(py_retval);')
def register_callback_classes(out, callbacks):
for callback_impl_num, template_parameters in enumerate(callbacks):
cls_name = "ns3::Callback< %s >" % ', '.join(template_parameters)
#print >> sys.stderr, "***** trying to register callback: %r" % cls_name
class_name = "PythonCallbackImpl%i" % callback_impl_num
class PythonCallbackParameter(Parameter):
"Class handlers"
CTYPES = [cls_name]
print("***** registering callback handler: %r (%r)" % (ctypeparser.normalize_type_string(cls_name), cls_name), file=sys.stderr)
DIRECTIONS = [Parameter.DIRECTION_IN]
PYTHON_CALLBACK_IMPL_NAME = class_name
TEMPLATE_ARGS = template_parameters
DISABLED = False
def convert_python_to_c(self, wrapper):
"parses python args to get C++ value"
assert isinstance(wrapper, typehandlers.ForwardWrapperBase)
if self.DISABLED:
raise CodeGenerationError("wrapper could not be generated")
if self.default_value is None:
py_callback = wrapper.declarations.declare_variable('PyObject*', self.name)
wrapper.parse_params.add_parameter('O', ['&'+py_callback], self.name)
wrapper.before_call.write_error_check(
'!PyCallable_Check(%s)' % py_callback,
'PyErr_SetString(PyExc_TypeError, "parameter \'%s\' must be callbale");' % self.name)
callback_impl = wrapper.declarations.declare_variable(
'ns3::Ptr<%s>' % self.PYTHON_CALLBACK_IMPL_NAME,
'%s_cb_impl' % self.name)
wrapper.before_call.write_code("%s = ns3::Create<%s> (%s);"
% (callback_impl, self.PYTHON_CALLBACK_IMPL_NAME, py_callback))
wrapper.call_params.append(
'ns3::Callback<%s> (%s)' % (', '.join(self.TEMPLATE_ARGS), callback_impl))
else:
py_callback = wrapper.declarations.declare_variable('PyObject*', self.name, 'NULL')
wrapper.parse_params.add_parameter('O', ['&'+py_callback], self.name, optional=True)
value = wrapper.declarations.declare_variable(
'ns3::Callback<%s>' % ', '.join(self.TEMPLATE_ARGS),
self.name+'_value',
self.default_value)
wrapper.before_call.write_code("if (%s) {" % (py_callback,))
wrapper.before_call.indent()
wrapper.before_call.write_error_check(
'!PyCallable_Check(%s)' % py_callback,
'PyErr_SetString(PyExc_TypeError, "parameter \'%s\' must be callbale");' % self.name)
wrapper.before_call.write_code("%s = ns3::Callback<%s> (ns3::Create<%s> (%s));"
% (value, ', '.join(self.TEMPLATE_ARGS),
self.PYTHON_CALLBACK_IMPL_NAME, py_callback))
wrapper.before_call.unindent()
wrapper.before_call.write_code("}") # closes: if (py_callback) {
wrapper.call_params.append(value)
def convert_c_to_python(self, wrapper):
raise typehandlers.NotSupportedError("Reverse wrappers for ns3::Callback<...> types "
"(python using callbacks defined in C++) not implemented.")
def generate_callback_classes(module, callbacks):
out = module.after_forward_declarations
for callback_impl_num, template_parameters in enumerate(callbacks):
sink = MemoryCodeSink()
cls_name = "ns3::Callback< %s >" % ', '.join(template_parameters)
#print >> sys.stderr, "***** trying to register callback: %r" % cls_name
class_name = "PythonCallbackImpl%i" % callback_impl_num
sink.writeln('''
class %s : public ns3::CallbackImpl<%s>
{
public:
PyObject *m_callback;
%s(PyObject *callback)
{
Py_INCREF(callback);
m_callback = callback;
}
virtual ~%s()
{
PyGILState_STATE __py_gil_state;
__py_gil_state = (PyEval_ThreadsInitialized() ? PyGILState_Ensure() : (PyGILState_STATE) 0);
Py_DECREF(m_callback);
m_callback = NULL;
PyGILState_Release(__py_gil_state);
}
virtual bool IsEqual(ns3::Ptr<const ns3::CallbackImplBase> other_base) const
{
const %s *other = dynamic_cast<const %s*> (ns3::PeekPointer (other_base));
if (other != NULL)
return (other->m_callback == m_callback);
else
return false;
}
''' % (class_name, ', '.join(template_parameters), class_name, class_name, class_name, class_name))
sink.indent()
callback_return = template_parameters[0]
return_ctype = ctypeparser.parse_type(callback_return)
if ('const' in return_ctype.remove_modifiers()):
kwargs = {'is_const': True}
else:
kwargs = {}
try:
return_type = ReturnValue.new(str(return_ctype), **kwargs)
except (typehandlers.TypeLookupError, typehandlers.TypeConfigurationError) as ex:
warnings.warn("***** Unable to register callback; Return value '%s' error (used in %s): %r"
% (callback_return, cls_name, ex),
Warning)
continue
arguments = []
ok = True
callback_parameters = [arg for arg in template_parameters[1:] if arg != 'ns3::empty']
for arg_num, arg_type in enumerate(callback_parameters):
arg_name = 'arg%i' % (arg_num+1)
param_ctype = ctypeparser.parse_type(arg_type)
if ('const' in param_ctype.remove_modifiers()):
kwargs = {'is_const': True}
else:
kwargs = {}
try:
arguments.append(Parameter.new(str(param_ctype), arg_name, **kwargs))
except (typehandlers.TypeLookupError, typehandlers.TypeConfigurationError) as ex:
warnings.warn("***** Unable to register callback; parameter '%s %s' error (used in %s): %r"
% (arg_type, arg_name, cls_name, ex),
Warning)
ok = False
if not ok:
try:
typehandlers.return_type_matcher.lookup(cls_name)[0].DISABLED = True
except typehandlers.TypeLookupError:
pass
try:
typehandlers.param_type_matcher.lookup(cls_name)[0].DISABLED = True
except typehandlers.TypeLookupError:
pass
continue
wrapper = CallbackImplProxyMethod(return_type, arguments)
wrapper.generate(sink, 'operator()', decl_modifiers=[])
sink.unindent()
sink.writeln('};\n')
print("Flushing to ", out, file=sys.stderr)
sink.flush_to(out)
# def write_preamble(out):
# pybindgen.write_preamble(out)
# out.writeln("#include \"ns3/everything.h\"")
def Simulator_customizations(module):
Simulator = module['ns3::Simulator']
## Simulator::Schedule(delay, callback, ...user..args...)
Simulator.add_custom_method_wrapper("Schedule", "_wrap_Simulator_Schedule",
flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
## Simulator::ScheduleNow(callback, ...user..args...)
Simulator.add_custom_method_wrapper("ScheduleNow", "_wrap_Simulator_ScheduleNow",
flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
## Simulator::ScheduleDestroy(callback, ...user..args...)
Simulator.add_custom_method_wrapper("ScheduleDestroy", "_wrap_Simulator_ScheduleDestroy",
flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
Simulator.add_custom_method_wrapper("Run", "_wrap_Simulator_Run",
flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
def CommandLine_customizations(module):
CommandLine = module['ns3::CommandLine']
CommandLine.add_method('Parse', None, [ArgvParam(None, 'argv')],
is_static=False)
CommandLine.add_custom_method_wrapper("AddValue", "_wrap_CommandLine_AddValue",
flags=["METH_VARARGS", "METH_KEYWORDS"])
def Object_customizations(module):
## ---------------------------------------------------------------------
## Here we generate custom constructor code for all classes that
## derive from ns3::Object. The custom constructors are needed in
## order to support kwargs only and to translate kwargs into ns3
## attributes, etc.
## ---------------------------------------------------------------------
try:
Object = module['ns3::Object']
except KeyError:
return
## add a GetTypeId method to all generatd helper classes
def helper_class_hook(helper_class):
decl = """
static ns3::TypeId GetTypeId (void)
{
static ns3::TypeId tid = ns3::TypeId ("%s")
.SetParent< %s > ()
;
return tid;
}""" % (helper_class.name, helper_class.class_.full_name)
helper_class.add_custom_method(decl)
helper_class.add_post_generation_code(
"NS_OBJECT_ENSURE_REGISTERED (%s);" % helper_class.name)
Object.add_helper_class_hook(helper_class_hook)
def ns3_object_instance_creation_function(cpp_class, code_block, lvalue,
parameters, construct_type_name):
assert lvalue
assert not lvalue.startswith('None')
if cpp_class.cannot_be_constructed:
raise CodeGenerationError("%s cannot be constructed (%s)"
% cpp_class.full_name)
if cpp_class.incomplete_type:
raise CodeGenerationError("%s cannot be constructed (incomplete type)"
% cpp_class.full_name)
code_block.write_code("%s = new %s(%s);" % (lvalue, construct_type_name, parameters))
code_block.write_code("%s->Ref ();" % (lvalue))
def ns3_object_post_instance_creation_function(cpp_class, code_block, lvalue,
parameters, construct_type_name):
code_block.write_code("ns3::CompleteConstruct(%s);" % (lvalue, ))
Object.set_instance_creation_function(ns3_object_instance_creation_function)
Object.set_post_instance_creation_function(ns3_object_post_instance_creation_function)
def Attribute_customizations(module):
# Fix up for the "const AttributeValue &v = EmptyAttribute()"
# case, as used extensively by helper classes.
# Here's why we need to do this: pybindgen.gccxmlscanner, when
# scanning parameter default values, is only provided with the
# value as a simple C expression string. (py)gccxml does not
# report the type of the default value.
# As a workaround, here we iterate over all parameters of all
# methods of all classes and tell pybindgen what is the type of
# the default value for attributes.
for cls in module.classes:
for meth in cls.get_all_methods():
for param in meth.parameters:
if isinstance(param, cppclass.CppClassRefParameter):
if param.cpp_class.name == 'AttributeValue' \
and param.default_value is not None \
and param.default_value_type is None:
param.default_value_type = 'ns3::EmptyAttributeValue'
def TypeId_customizations(module):
TypeId = module['ns3::TypeId']
TypeId.add_custom_method_wrapper("LookupByNameFailSafe", "_wrap_TypeId_LookupByNameFailSafe",
flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
def add_std_ofstream(module):
module.add_include('<fstream>')
ostream = module.add_class('ostream', foreign_cpp_namespace='::std')
ostream.set_cannot_be_constructed("abstract base class")
ofstream = module.add_class('ofstream', foreign_cpp_namespace='::std', parent=ostream)
ofstream.add_enum('openmode', [
('app', 'std::ios_base::app'),
('ate', 'std::ios_base::ate'),
('binary', 'std::ios_base::binary'),
('in', 'std::ios_base::in'),
('out', 'std::ios_base::out'),
('trunc', 'std::ios_base::trunc'),
])
ofstream.add_constructor([Parameter.new("const char *", 'filename'),
Parameter.new("::std::ofstream::openmode", 'mode', default_value="std::ios_base::out")])
ofstream.add_method('close', None, [])
add_std_ios_openmode(module)
class IosOpenmodeParam(Parameter):
DIRECTIONS = [Parameter.DIRECTION_IN]
CTYPES = ['std::ios::openmode', 'std::_Ios_Openmode']
def convert_c_to_python(self, wrapper):
assert isinstance(wrapper, ReverseWrapperBase)
wrapper.build_params.add_parameter('i', [self.value])
def convert_python_to_c(self, wrapper):
assert isinstance(wrapper, ForwardWrapperBase)
name = wrapper.declarations.declare_variable("std::ios::openmode", self.name, self.default_value)
wrapper.parse_params.add_parameter('i', ['&'+name], self.name, optional=bool(self.default_value))
wrapper.call_params.append(name)
def add_std_ios_openmode(module):
for flag in 'in', 'out', 'ate', 'app', 'trunc', 'binary':
module.after_init.write_code('PyModule_AddIntConstant(m, (char *) "STD_IOS_%s", std::ios::%s);'
% (flag.upper(), flag))
def add_ipv4_address_tp_hash(module):
module.body.writeln('''
long
_ns3_Ipv4Address_tp_hash (PyObject *obj)
{
PyNs3Ipv4Address *addr = reinterpret_cast<PyNs3Ipv4Address *> (obj);
return static_cast<long> (ns3::Ipv4AddressHash () (*addr->obj));
}
''')
module.header.writeln('long _ns3_Ipv4Address_tp_hash (PyObject *obj);')
module['Ipv4Address'].pytype.slots['tp_hash'] = "_ns3_Ipv4Address_tp_hash"
-281
View File
@@ -1,281 +0,0 @@
#! /usr/bin/env python
import sys
import os.path
import pybindgen.settings
from pybindgen.gccxmlparser import ModuleParser, PygenClassifier, PygenSection, WrapperWarning, find_declaration_from_name
from pybindgen.typehandlers.codesink import FileCodeSink
from pygccxml.declarations import templates
from pygccxml.declarations.enumeration import enumeration_t
from pygccxml.declarations.class_declaration import class_t
from pygccxml.declarations.calldef import free_function_t, member_function_t, constructor_t, calldef_t
## we need the smart pointer type transformation to be active even
## during gccxml scanning.
import ns3modulegen_core_customizations
## silence gccxmlparser errors; we only want error handling in the
## generated python script, not while scanning.
class ErrorHandler(pybindgen.settings.ErrorHandler):
def handle_error(self, dummy_wrapper, dummy_exception, dummy_traceback_):
return True
pybindgen.settings.error_handler = ErrorHandler()
import warnings
warnings.filterwarnings(category=WrapperWarning, action='ignore')
import ns3modulescan
type_annotations = ns3modulescan.type_annotations
def get_ns3_relative_path(path):
l = []
head = path
while head:
new_head, tail = os.path.split(head)
if new_head == head:
raise ValueError
head = new_head
if tail == 'ns3':
return os.path.join(*l)
l.insert(0, tail)
raise AssertionError("is the path %r inside ns3?!" % path)
class PreScanHook:
def __init__(self, headers_map, module):
self.headers_map = headers_map
self.module = module
def __call__(self, module_parser,
pygccxml_definition,
global_annotations,
parameter_annotations):
try:
ns3_header = get_ns3_relative_path(pygccxml_definition.location.file_name)
except ValueError: # the header is not from ns3
return # ignore the definition, it's not ns-3 def.
definition_module = self.headers_map[ns3_header]
## Note: we don't include line numbers in the comments because
## those numbers are very likely to change frequently, which would
## cause needless changes, since the generated python files are
## kept under version control.
#global_annotations['pygen_comment'] = "%s:%i: %s" % \
# (ns3_header, pygccxml_definition.location.line, pygccxml_definition)
global_annotations['pygen_comment'] = "%s (module %r): %s" % \
(ns3_header, definition_module, pygccxml_definition)
## handle ns3::Object::GetObject (left to its own devices,
## pybindgen will generate a mangled name containing the template
## argument type name).
if isinstance(pygccxml_definition, member_function_t) \
and pygccxml_definition.parent.name == 'Object' \
and pygccxml_definition.name == 'GetObject':
template_args = templates.args(pygccxml_definition.demangled_name)
if template_args == ['ns3::Object']:
global_annotations['template_instance_names'] = 'ns3::Object=>GetObject'
## Don't wrap Simulator::Schedule* (manually wrapped)
if isinstance(pygccxml_definition, member_function_t) \
and pygccxml_definition.parent.name == 'Simulator' \
and pygccxml_definition.name.startswith('Schedule'):
global_annotations['ignore'] = None
# manually wrapped
if isinstance(pygccxml_definition, member_function_t) \
and pygccxml_definition.parent.name == 'Simulator' \
and pygccxml_definition.name == 'Run':
global_annotations['ignore'] = True
## http://www.gccxml.org/Bug/view.php?id=9915
if isinstance(pygccxml_definition, calldef_t):
for arg in pygccxml_definition.arguments:
if arg.default_value is None:
continue
elif arg.default_value == "ns3::MilliSeconds( )":
arg.default_value = "ns3::MilliSeconds(0)"
elif arg.default_value == "ns3::Seconds( )":
arg.default_value = "ns3::Seconds(0)"
## classes
if isinstance(pygccxml_definition, class_t):
print >> sys.stderr, pygccxml_definition
# no need for helper classes to allow subclassing in Python, I think...
#if pygccxml_definition.name.endswith('Helper'):
# global_annotations['allow_subclassing'] = 'false'
#
# If a class is template instantiation, even if the
# template was defined in some other module, if a template
# argument belongs to this module then the template
# instantiation will belong to this module.
#
if templates.is_instantiation(pygccxml_definition.decl_string):
cls_name, template_parameters = templates.split(pygccxml_definition.name)
template_parameters_decls = [find_declaration_from_name(module_parser.global_ns, templ_param)
for templ_param in template_parameters]
#print >> sys.stderr, "********************", cls_name, repr(template_parameters_decls)
template_parameters_modules = []
for templ in template_parameters_decls:
if not hasattr(templ, 'location'):
continue
try:
h = get_ns3_relative_path(templ.location.file_name)
except ValueError:
continue
template_parameters_modules.append(self.headers_map[h])
for templ_mod in template_parameters_modules:
if templ_mod == self.module:
definition_module = templ_mod
break
#print >> sys.stderr, "********************", cls_name, repr(template_parameters_modules)
if definition_module != self.module:
global_annotations['import_from_module'] = 'ns.%s' % (definition_module.replace('-', '_'),)
if pygccxml_definition.decl_string.startswith('::ns3::SimpleRefCount<'):
global_annotations['incref_method'] = 'Ref'
global_annotations['decref_method'] = 'Unref'
global_annotations['peekref_method'] = 'GetReferenceCount'
global_annotations['automatic_type_narrowing'] = 'true'
return
if pygccxml_definition.decl_string.startswith('::ns3::Callback<'):
# manually handled in ns3modulegen_core_customizations.py
global_annotations['ignore'] = None
return
if pygccxml_definition.decl_string.startswith('::ns3::TracedCallback<'):
global_annotations['ignore'] = None
return
if pygccxml_definition.decl_string.startswith('::ns3::Ptr<'):
# handled by pybindgen "type transformation"
global_annotations['ignore'] = None
return
# table driven class customization
try:
annotations = type_annotations[pygccxml_definition.decl_string]
except KeyError:
pass
else:
global_annotations.update(annotations)
## enums
if isinstance(pygccxml_definition, enumeration_t):
if definition_module != self.module:
global_annotations['import_from_module'] = 'ns.%s' % definition_module
## free functions
if isinstance(pygccxml_definition, free_function_t):
if definition_module != self.module:
global_annotations['ignore'] = None
return
if pygccxml_definition.name == 'PeekPointer':
global_annotations['ignore'] = None
return
## table driven methods/constructors/functions customization
if isinstance(pygccxml_definition, (free_function_t, member_function_t, constructor_t)):
try:
annotations = type_annotations[str(pygccxml_definition)]
except KeyError:
pass
else:
for key,value in annotations.items():
if key == 'params':
parameter_annotations.update (value)
del annotations['params']
global_annotations.update(annotations)
# def post_scan_hook(dummy_module_parser, dummy_pygccxml_definition, pybindgen_wrapper):
# ## classes
# if isinstance(pybindgen_wrapper, CppClass):
# if pybindgen_wrapper.name.endswith('Checker'):
# print >> sys.stderr, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", pybindgen_wrapper
# #pybindgen_wrapper.set_instance_creation_function(AttributeChecker_instance_creation_function)
def scan_callback_classes(module_parser, callback_classes_file):
callback_classes_file.write("callback_classes = [\n")
for cls in module_parser.module_namespace.classes(function=module_parser.location_filter,
recursive=False):
if not cls.name.startswith("Callback<"):
continue
assert templates.is_instantiation(cls.decl_string), "%s is not a template instantiation" % cls
dummy_cls_name, template_parameters = templates.split(cls.decl_string)
callback_classes_file.write(" %r,\n" % template_parameters)
callback_classes_file.write("]\n")
def ns3_module_scan(top_builddir, module_name, headers_map, output_file_name, cflags):
module_parser = ModuleParser('ns.%s' % module_name.replace('-', '_'), 'ns3')
module_parser.add_pre_scan_hook(PreScanHook(headers_map, module_name))
#module_parser.add_post_scan_hook(post_scan_hook)
gccxml_options = dict(
include_paths=[top_builddir],
define_symbols={
#'NS3_ASSERT_ENABLE': None,
#'NS3_LOG_ENABLE': None,
},
cflags=('--gccxml-cxxflags "%s -DPYTHON_SCAN"' % cflags)
)
try:
os.unlink(output_file_name)
except OSError:
pass
try:
os.makedirs(os.path.dirname(output_file_name))
except OSError:
pass
output_file = open(output_file_name, "wt")
output_sink = FileCodeSink(output_file)
# if there exists a scan-header.h file in src/<module>/bindings,
# scan it, otherwise scan ns3/xxxx-module.h.
scan_header = os.path.join(os.path.dirname(output_file_name), "scan-header.h")
if not os.path.exists(scan_header):
scan_header = os.path.join(top_builddir, "ns3", "%s-module.h" % module_name)
module_parser.parse_init([scan_header],
None, whitelist_paths=[top_builddir],
#includes=['"ns3/everything.h"'],
pygen_sink=output_sink,
gccxml_options=gccxml_options)
module_parser.scan_types()
callback_classes_file = open(os.path.join(os.path.dirname(output_file_name), "callbacks_list.py"), "wt")
scan_callback_classes(module_parser, callback_classes_file)
callback_classes_file.close()
module_parser.scan_methods()
module_parser.scan_functions()
module_parser.parse_finalize()
output_file.close()
os.chmod(output_file_name, 0400)
if __name__ == '__main__':
if len(sys.argv) != 6:
print "ns3modulescan-modular.py top_builddir module_path module_headers output_file_name cflags"
sys.exit(1)
ns3_module_scan(sys.argv[1], sys.argv[2], eval(sys.argv[3]), sys.argv[4], sys.argv[5])
sys.exit(0)
-333
View File
@@ -1,333 +0,0 @@
#! /usr/bin/env python
import sys
import os.path
import pybindgen.settings
from pybindgen.gccxmlparser import ModuleParser, PygenClassifier, PygenSection, WrapperWarning
from pybindgen.typehandlers.codesink import FileCodeSink
from pygccxml.declarations import templates
from pygccxml.declarations.class_declaration import class_t
from pygccxml.declarations.calldef import free_function_t, member_function_t, constructor_t, calldef_t
## we need the smart pointer type transformation to be active even
## during gccxml scanning.
import ns3modulegen_core_customizations
## silence gccxmlparser errors; we only want error handling in the
## generated python script, not while scanning.
class ErrorHandler(pybindgen.settings.ErrorHandler):
def handle_error(self, dummy_wrapper, dummy_exception, dummy_traceback_):
return True
pybindgen.settings.error_handler = ErrorHandler()
import warnings
warnings.filterwarnings(category=WrapperWarning, action='ignore')
type_annotations = {
'::ns3::AttributeChecker': {
'automatic_type_narrowing': 'true',
'allow_subclassing': 'false',
},
'::ns3::AttributeValue': {
'automatic_type_narrowing': 'true',
'allow_subclassing': 'false',
},
'::ns3::CommandLine': {
'allow_subclassing': 'true', # needed so that AddValue is able to set attributes on the object
},
'::ns3::NscTcpL4Protocol': {
'ignore': 'true', # this class is implementation detail
},
'ns3::RandomVariable::RandomVariable(ns3::RandomVariableBase const & variable) [constructor]': {
'ignore': None,
},
'ns3::RandomVariableBase * ns3::RandomVariable::Peek() const [member function]': {
'ignore': None,
},
'void ns3::RandomVariable::GetSeed(uint32_t * seed) const [member function]': {
'params': {'seed':{'direction':'out',
'array_length':'6'}}
},
'bool ns3::TypeId::LookupAttributeByName(std::string name, ns3::TypeId::AttributeInformation * info) const [member function]': {
'params': {'info':{'transfer_ownership': 'false'}}
},
'static bool ns3::TypeId::LookupByNameFailSafe(std::string name, ns3::TypeId * tid) [member function]': {
'ignore': None, # manually wrapped in
},
'bool ns3::TraceSourceAccessor::ConnectWithoutContext(ns3::ObjectBase * obj, ns3::CallbackBase const & cb) const [member function]': {
'params': {'obj': {'transfer_ownership':'false'}}
},
'bool ns3::TraceSourceAccessor::Connect(ns3::ObjectBase * obj, std::string context, ns3::CallbackBase const & cb) const [member function]': {
'params': {'obj': {'transfer_ownership':'false'}}
},
'bool ns3::TraceSourceAccessor::DisconnectWithoutContext(ns3::ObjectBase * obj, ns3::CallbackBase const & cb) const [member function]': {
'params': {'obj': {'transfer_ownership':'false'}}
},
'bool ns3::TraceSourceAccessor::Disconnect(ns3::ObjectBase * obj, std::string context, ns3::CallbackBase const & cb) const [member function]': {
'params': {'obj': {'transfer_ownership':'false'}}
},
'bool ns3::AttributeAccessor::Set(ns3::ObjectBase * object, ns3::AttributeValue const & value) const [member function]': {
'params': {'object': {'transfer_ownership':'false'}}
},
'ns3::EmpiricalVariable::EmpiricalVariable(ns3::RandomVariableBase const & variable) [constructor]': {
'ignore': None
},
'static ns3::AttributeList * ns3::AttributeList::GetGlobal() [member function]': {
'caller_owns_return': 'false'
},
'void ns3::CommandLine::Parse(int argc, char * * argv) const [member function]': {
'ignore': None # manually wrapped
},
'extern void ns3::PythonCompleteConstruct(ns3::Ptr<ns3::Object> object, ns3::TypeId typeId, ns3::AttributeList const & attributes) [free function]': {
'ignore': None # used transparently by, should not be wrapped
},
'ns3::Ptr<ns3::Ipv4RoutingProtocol> ns3::Ipv4ListRouting::GetRoutingProtocol(uint32_t index, int16_t & priority) const [member function]': {
'params': {'priority':{'direction':'out'}}
},
'ns3::Ipv4RoutingTableEntry * ns3::GlobalRouter::GetInjectedRoute(uint32_t i) [member function]': {
'params': {'return': { 'caller_owns_return': 'false',}},
},
'ns3::Ipv4RoutingTableEntry * ns3::Ipv4GlobalRouting::GetRoute(uint32_t i) const [member function]': {
'params': {'return': { 'caller_owns_return': 'false',}},
},
'::ns3::TestCase': {
'ignore': 'true', # we don't need to write test cases in Python
},
'::ns3::TestRunner': {
'ignore': 'true', # we don't need to write test cases in Python
},
'::ns3::TestSuite': {
'ignore': 'true', # we don't need to write test cases in Python
},
}
def get_ns3_relative_path(path):
l = []
head = path
while head:
head, tail = os.path.split(head)
if tail == 'ns3':
return os.path.join(*l)
l.insert(0, tail)
raise AssertionError("is the path %r inside ns3?!" % path)
def pre_scan_hook(dummy_module_parser,
pygccxml_definition,
global_annotations,
parameter_annotations):
ns3_header = get_ns3_relative_path(pygccxml_definition.location.file_name)
## Note: we don't include line numbers in the comments because
## those numbers are very likely to change frequently, which would
## cause needless changes, since the generated python files are
## kept under version control.
#global_annotations['pygen_comment'] = "%s:%i: %s" % \
# (ns3_header, pygccxml_definition.location.line, pygccxml_definition)
global_annotations['pygen_comment'] = "%s: %s" % \
(ns3_header, pygccxml_definition)
## handle ns3::Object::GetObject (left to its own devices,
## pybindgen will generate a mangled name containing the template
## argument type name).
if isinstance(pygccxml_definition, member_function_t) \
and pygccxml_definition.parent.name == 'Object' \
and pygccxml_definition.name == 'GetObject':
template_args = templates.args(pygccxml_definition.demangled_name)
if template_args == ['ns3::Object']:
global_annotations['template_instance_names'] = 'ns3::Object=>GetObject'
## Don't wrap Simulator::Schedule* (manually wrapped)
if isinstance(pygccxml_definition, member_function_t) \
and pygccxml_definition.parent.name == 'Simulator' \
and pygccxml_definition.name.startswith('Schedule'):
global_annotations['ignore'] = None
# manually wrapped
if isinstance(pygccxml_definition, member_function_t) \
and pygccxml_definition.parent.name == 'Simulator' \
and pygccxml_definition.name == 'Run':
global_annotations['ignore'] = True
## http://www.gccxml.org/Bug/view.php?id=9915
if isinstance(pygccxml_definition, calldef_t):
for arg in pygccxml_definition.arguments:
if arg.default_value is None:
continue
if "ns3::MilliSeconds( )" == arg.default_value:
arg.default_value = "ns3::MilliSeconds(0)"
if "ns3::Seconds( )" == arg.default_value:
arg.default_value = "ns3::Seconds(0)"
## classes
if isinstance(pygccxml_definition, class_t):
# no need for helper classes to allow subclassing in Python, I think...
#if pygccxml_definition.name.endswith('Helper'):
# global_annotations['allow_subclassing'] = 'false'
if pygccxml_definition.decl_string.startswith('::ns3::SimpleRefCount<'):
global_annotations['incref_method'] = 'Ref'
global_annotations['decref_method'] = 'Unref'
global_annotations['peekref_method'] = 'GetReferenceCount'
global_annotations['automatic_type_narrowing'] = 'true'
return
if pygccxml_definition.decl_string.startswith('::ns3::Callback<'):
# manually handled in ns3modulegen_core_customizations.py
global_annotations['ignore'] = None
return
if pygccxml_definition.decl_string.startswith('::ns3::TracedCallback<'):
global_annotations['ignore'] = None
return
if pygccxml_definition.decl_string.startswith('::ns3::Ptr<'):
# handled by pybindgen "type transformation"
global_annotations['ignore'] = None
return
# table driven class customization
try:
annotations = type_annotations[pygccxml_definition.decl_string]
except KeyError:
pass
else:
global_annotations.update(annotations)
## free functions
if isinstance(pygccxml_definition, free_function_t):
if pygccxml_definition.name == 'PeekPointer':
global_annotations['ignore'] = None
return
## table driven methods/constructors/functions customization
if isinstance(pygccxml_definition, (free_function_t, member_function_t, constructor_t)):
try:
annotations = type_annotations[str(pygccxml_definition)]
except KeyError:
pass
else:
for key,value in annotations.items():
if key == 'params':
parameter_annotations.update (value)
del annotations['params']
global_annotations.update(annotations)
# def post_scan_hook(dummy_module_parser, dummy_pygccxml_definition, pybindgen_wrapper):
# ## classes
# if isinstance(pybindgen_wrapper, CppClass):
# if pybindgen_wrapper.name.endswith('Checker'):
# print >> sys.stderr, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", pybindgen_wrapper
# #pybindgen_wrapper.set_instance_creation_function(AttributeChecker_instance_creation_function)
def scan_callback_classes(module_parser, callback_classes_file):
callback_classes_file.write("callback_classes = [\n")
for cls in module_parser.module_namespace.classes(function=module_parser.location_filter,
recursive=False):
if not cls.name.startswith("Callback<"):
continue
assert templates.is_instantiation(cls.decl_string), "%s is not a template instantiation" % cls
dummy_cls_name, template_parameters = templates.split(cls.decl_string)
callback_classes_file.write(" %r,\n" % template_parameters)
callback_classes_file.write("]\n")
class MyPygenClassifier(PygenClassifier):
def __init__(self, headers_map, section_precendences):
self.headers_map = headers_map
self.section_precendences = section_precendences
def classify(self, pygccxml_definition):
name = os.path.basename(pygccxml_definition.location.file_name)
try:
return self.headers_map[name]
except KeyError:
return '__main__'
def get_section_precedence(self, section_name):
if section_name == '__main__':
return -1
return self.section_precendences[section_name]
def ns3_module_scan(top_builddir, pygen_file_name, everything_h, cflags):
ns3_modules = eval(sys.stdin.readline())
## do a topological sort on the modules graph
from topsort import topsort
graph = []
module_names = ns3_modules.keys()
module_names.sort()
for ns3_module_name in module_names:
ns3_module_deps = list(ns3_modules[ns3_module_name][0])
ns3_module_deps.sort()
for dep in ns3_module_deps:
graph.append((dep, ns3_module_name))
sorted_ns3_modules = topsort(graph)
#print >> sys.stderr, "******* topological sort: ", sorted_ns3_modules
sections = [PygenSection('__main__', FileCodeSink(open(pygen_file_name, "wt")))]
headers_map = {} # header_name -> section_name
section_precendences = {} # section_name -> precedence
for prec, ns3_module in enumerate(sorted_ns3_modules):
section_name = "ns3_module_%s" % ns3_module.replace('-', '_')
file_name = os.path.join(os.path.dirname(pygen_file_name), "%s.py" % section_name)
sections.append(PygenSection(section_name, FileCodeSink(open(file_name, "wt")),
section_name + "__local"))
for header in ns3_modules[ns3_module][1]:
headers_map[header] = section_name
section_precendences[section_name] = prec
module_parser = ModuleParser('ns3', 'ns3')
module_parser.add_pre_scan_hook(pre_scan_hook)
#module_parser.add_post_scan_hook(post_scan_hook)
gccxml_options = dict(
include_paths=[top_builddir],
define_symbols={
#'NS3_ASSERT_ENABLE': None,
#'NS3_LOG_ENABLE': None,
},
cflags=('--gccxml-cxxflags "%s -DPYTHON_SCAN"' % cflags)
)
module_parser.parse_init([everything_h],
None, whitelist_paths=[top_builddir, os.path.dirname(everything_h)],
#includes=['"ns3/everything.h"'],
pygen_sink=sections,
pygen_classifier=MyPygenClassifier(headers_map, section_precendences),
gccxml_options=gccxml_options)
module_parser.scan_types()
callback_classes_file = open(os.path.join(os.path.dirname(pygen_file_name), "callbacks_list.py"), "wt")
scan_callback_classes(module_parser, callback_classes_file)
callback_classes_file.close()
module_parser.scan_methods()
module_parser.scan_functions()
module_parser.parse_finalize()
for section in sections:
section.code_sink.file.close()
if __name__ == '__main__':
ns3_module_scan(sys.argv[1], sys.argv[3], sys.argv[2], sys.argv[4])
-1
View File
@@ -1 +0,0 @@
-1
View File
@@ -1 +0,0 @@
placeholder
-909
View File
@@ -1,909 +0,0 @@
# Copyright (c) 2007 RADLogic
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""Provide various handy Python functions.
Running this script directly will execute the doctests.
Functions:
int2bin(i, n) -- Convert integer to binary string.
bin2int(bin_string) -- Convert binary string to integer.
reverse(input_string) -- Reverse a string.
transpose(matrix) -- Transpose a list of lists.
polygon_area(points_list) -- Calculate the area of an arbitrary polygon.
timestamp() -- Return string containing current time stamp.
pt2str(point) -- Return prettier string version of point tuple.
gcf(a, b) -- Return the greatest common factor of two numbers.
lcm(a, b) -- Return the least common multiple of two numbers.
permutations(input_list) -- Generate all permutations of a list of items.
reduce_fraction(fraction) -- Reduce fraction (num, denom) to simplest form.
quantile(l, p) -- Return p quantile of list l. E.g. p=0.25 for q1.
trim(l) -- Discard values in list more than 1.5*IQR outside IQR.
nice_units(value) -- Return value converted to human readable units.
uniquify(seq) -- Return sequence with duplicate items in sequence seq removed.
reverse_dict(d) -- Return the dictionary with the items as keys and vice-versa.
lsb(x, n) -- Return the n least significant bits of x.
gray_encode(i) -- Gray encode the given integer.
random_vec(bits, max_value=None) -- Return a random binary vector.
binary_range(bits) -- Return list of all possible binary numbers width=bits.
float_range([start], stop, [step]) -- Return range of floats.
find_common_fixes(s1, s2) -- Find common (prefix, suffix) of two strings.
is_rotated(seq1, seq2) -- Return true if the list is a rotation of other list.
getmodule(obj) -- Return the module that contains the object definition of obj.
(use inspect.getmodule instead, though)
get_args(argv) -- Store command-line args in a dictionary.
This module requires Python >= 2.2
"""
__author__ = 'Tim Wegener <twegener@radlogic.com.au>'
__date__ = '$Date: 2007/03/27 03:15:06 $'
__version__ = '$Revision: 0.45 $'
__credits__ = """
David Chandler, for polygon area algorithm.
(http://www.davidchandler.com/AreaOfAGeneralPolygon.pdf)
"""
import re
import sys
import time
import random
try:
True, False
except NameError:
True, False = (1==1, 0==1)
def int2bin(i, n):
"""Convert decimal integer i to n-bit binary number (string).
>>> int2bin(0, 8)
'00000000'
>>> int2bin(123, 8)
'01111011'
>>> int2bin(123L, 8)
'01111011'
>>> int2bin(15, 2)
Traceback (most recent call last):
ValueError: Value too large for given number of bits.
"""
hex2bin = {'0': '0000', '1': '0001', '2': '0010', '3': '0011',
'4': '0100', '5': '0101', '6': '0110', '7': '0111',
'8': '1000', '9': '1001', 'a': '1010', 'b': '1011',
'c': '1100', 'd': '1101', 'e': '1110', 'f': '1111'}
# Convert to hex then map each hex digit to binary equivalent.
result = ''.join([hex2bin[x] for x in hex(i).lower().replace('l','')[2:]])
# Shrink result to appropriate length.
# Raise an error if the value is changed by the truncation.
if '1' in result[:-n]:
raise ValueError("Value too large for given number of bits.")
result = result[-n:]
# Zero-pad if length longer than mapped result.
result = '0'*(n-len(result)) + result
return result
def bin2int(bin_string):
"""Convert binary number string to decimal integer.
Note: Python > v2 has int(bin_string, 2)
>>> bin2int('1111')
15
>>> bin2int('0101')
5
"""
## result = 0
## bin_list = list(bin_string)
## if len(filter(lambda x: x in ('1','0'), bin_list)) < len(bin_list):
## raise Exception ("bin2int: Error - not a binary number: %s"
## % bin_string)
## bit_list = map(int, bin_list)
## bit_list.reverse() # Make most significant bit have highest index.
## for bit_place in range(len(bit_list)):
## result = result + ((2**bit_place) * bit_list[bit_place])
## return result
return int(bin_string, 2)
def reverse(input_string):
"""Reverse a string. Useful for strings of binary numbers.
>>> reverse('abc')
'cba'
"""
str_list = list(input_string)
str_list.reverse()
return ''.join(str_list)
def transpose(matrix):
"""Transpose a list of lists.
>>> transpose([['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']])
[['a', 'd', 'g'], ['b', 'e', 'h'], ['c', 'f', 'i']]
>>> transpose([['a', 'b', 'c'], ['d', 'e', 'f']])
[['a', 'd'], ['b', 'e'], ['c', 'f']]
>>> transpose([['a', 'b'], ['d', 'e'], ['g', 'h']])
[['a', 'd', 'g'], ['b', 'e', 'h']]
"""
result = zip(*matrix)
# Convert list of tuples to list of lists.
# map is faster than a list comprehension since it is being used with
# a built-in function as an argument.
result = map(list, result)
return result
def polygon_area(points_list, precision=100):
"""Calculate area of an arbitrary polygon using an algorithm from the web.
Return the area of the polygon as a positive float.
Arguments:
points_list -- list of point tuples [(x0, y0), (x1, y1), (x2, y2), ...]
(Unclosed polygons will be closed automatically.
precision -- Internal arithmetic precision (integer arithmetic).
>>> polygon_area([(0, 0), (0, 1), (1, 1), (1, 2), (2, 2), (2, 0), (0, 0)])
3.0
Credits:
Area of a General Polygon by David Chandler
http://www.davidchandler.com/AreaOfAGeneralPolygon.pdf
"""
# Scale up co-ordinates and convert them to integers.
for i in range(len(points_list)):
points_list[i] = (int(points_list[i][0] * precision),
int(points_list[i][1] * precision))
# Close polygon if not closed.
if points_list[-1] != points_list[0]:
points_list.append(points_list[0])
# Calculate area.
area = 0
for i in range(len(points_list)-1):
(x_i, y_i) = points_list[i]
(x_i_plus_1, y_i_plus_1) = points_list[i+1]
area = area + (x_i_plus_1 * y_i) - (y_i_plus_1 * x_i)
area = abs(area / 2)
# Unscale area.
area = float(area)/(precision**2)
return area
def timestamp():
"""Return string containing current time stamp.
Note: In Python 2 onwards can use time.asctime() with no arguments.
"""
return time.asctime()
def pt2str(point):
"""Return prettier string version of point tuple.
>>> pt2str((1.8, 1.9))
'(1.8, 1.9)'
"""
return "(%s, %s)" % (str(point[0]), str(point[1]))
def gcf(a, b, epsilon=1e-16):
"""Return the greatest common factor of a and b, using Euclidean algorithm.
Arguments:
a, b -- two numbers
If both numbers are integers return an integer result,
otherwise return a float result.
epsilon -- floats less than this magnitude are considered to be zero
(default: 1e-16)
Examples:
>>> gcf(12, 34)
2
>>> gcf(13.5, 4)
0.5
>>> gcf(-2, 4)
2
>>> gcf(5, 0)
5
By (a convenient) definition:
>>> gcf(0, 0)
0
"""
result = max(a, b)
remainder = min(a, b)
while remainder and abs(remainder) > epsilon:
new_remainder = result % remainder
result = remainder
remainder = new_remainder
return abs(result)
def lcm(a, b, precision=None):
"""Return the least common multiple of a and b, using the gcf function.
Arguments:
a, b -- two numbers. If both are integers return an integer result,
otherwise a return a float result.
precision -- scaling factor if a and/or b are floats.
>>> lcm(21, 6)
42
>>> lcm(2.5, 3.5)
17.5
>>> str(lcm(1.5e-8, 2.5e-8, precision=1e9))
'7.5e-08'
By (an arbitary) definition:
>>> lcm(0, 0)
0
"""
# Note: Dummy precision argument is for backwards compatibility.
# Do the division first.
# (See http://en.wikipedia.org/wiki/Least_common_multiple )
denom = gcf(a, b)
if denom == 0:
result = 0
else:
result = a * (b / denom)
return result
def permutations(input_list):
"""Return a list containing all permutations of the input list.
Note: This is a recursive function.
>>> perms = permutations(['a', 'b', 'c'])
>>> perms.sort()
>>> for perm in perms:
... print perm
['a', 'b', 'c']
['a', 'c', 'b']
['b', 'a', 'c']
['b', 'c', 'a']
['c', 'a', 'b']
['c', 'b', 'a']
"""
out_lists = []
if len(input_list) > 1:
# Extract first item in list.
item = input_list[0]
# Find all permutations of remainder of list. (Recursive call.)
sub_lists = permutations(input_list[1:])
# For every permutation of the sub list...
for sub_list in sub_lists:
# Insert the extracted first item at every position of the list.
for i in range(len(input_list)):
new_list = sub_list[:]
new_list.insert(i, item)
out_lists.append(new_list)
else:
# Termination condition: only one item in input list.
out_lists = [input_list]
return out_lists
def reduce_fraction(fraction):
"""Reduce fraction tuple to simplest form. fraction=(num, denom)
>>> reduce_fraction((14, 7))
(2, 1)
>>> reduce_fraction((-2, 4))
(-1, 2)
>>> reduce_fraction((0, 4))
(0, 1)
>>> reduce_fraction((4, 0))
(1, 0)
"""
(numerator, denominator) = fraction
common_factor = abs(gcf(numerator, denominator))
result = (numerator/common_factor, denominator/common_factor)
return result
def quantile(l, p):
"""Return p quantile of list l. E.g. p=0.25 for q1.
See:
http://rweb.stat.umn.edu/R/library/base/html/quantile.html
"""
l_sort = l[:]
l_sort.sort()
n = len(l)
r = 1 + ((n - 1) * p)
i = int(r)
f = r - i
if i < n:
result = (1-f)*l_sort[i-1] + f*l_sort[i]
else:
result = l_sort[i-1]
return result
def trim(l):
"""Discard values in list more than 1.5*IQR outside IQR.
(IQR is inter-quartile-range)
This function uses rad_util.quantile
1.5*IQR -- mild outlier
3*IQR -- extreme outlier
See:
http://wind.cc.whecn.edu/~pwildman/statnew/section_7_-_exploratory_data_analysis.htm
"""
l_sort = l[:]
l_sort.sort()
# Calculate medianscore (based on stats.py lmedianscore by Gary Strangman)
if len(l_sort) % 2 == 0:
# If even number of scores, average middle 2.
index = int(len(l_sort) / 2) # Integer division correct
median = float(l_sort[index] + l_sort[index-1]) / 2
else:
# int divsion gives mid value when count from 0
index = int(len(l_sort) / 2)
median = l_sort[index]
# Calculate IQR.
q1 = quantile(l_sort, 0.25)
q3 = quantile(l_sort, 0.75)
iqr = q3 - q1
iqr_extra = iqr * 1.5
def in_interval(x, i=iqr_extra, q1=q1, q3=q3):
return (x >= q1-i and x <= q3+i)
l_trimmed = [x for x in l_sort if in_interval(x)]
return l_trimmed
def nice_units(value, dp=0, sigfigs=None, suffix='', space=' ',
use_extra_prefixes=False, use_full_name=False, mode='si'):
"""Return value converted to human readable units eg milli, micro, etc.
Arguments:
value -- number in base units
dp -- number of decimal places to display (rounded)
sigfigs -- number of significant figures to display (rounded)
This overrides dp if set.
suffix -- optional unit suffix to append to unit multiplier
space -- seperator between value and unit multiplier (default: ' ')
use_extra_prefixes -- use hecto, deka, deci and centi as well if set.
(default: False)
use_full_name -- use full name for multiplier symbol,
e.g. milli instead of m
(default: False)
mode -- 'si' for SI prefixes, 'bin' for binary multipliers (1024, etc.)
(Default: 'si')
SI prefixes from:
http://physics.nist.gov/cuu/Units/prefixes.html
(Greek mu changed to u.)
Binary prefixes based on:
http://physics.nist.gov/cuu/Units/binary.html
>>> nice_units(2e-11)
'20 p'
>>> nice_units(2e-11, space='')
'20p'
"""
si_prefixes = {1e24: ('Y', 'yotta'),
1e21: ('Z', 'zetta'),
1e18: ('E', 'exa'),
1e15: ('P', 'peta'),
1e12: ('T', 'tera'),
1e9: ('G', 'giga'),
1e6: ('M', 'mega'),
1e3: ('k', 'kilo'),
1e-3: ('m', 'milli'),
1e-6: ('u', 'micro'),
1e-9: ('n', 'nano'),
1e-12: ('p', 'pico'),
1e-15: ('f', 'femto'),
1e-18: ('a', 'atto'),
1e-21: ('z', 'zepto'),
1e-24: ('y', 'yocto')
}
if use_extra_prefixes:
si_prefixes.update({1e2: ('h', 'hecto'),
1e1: ('da', 'deka'),
1e-1: ('d', 'deci'),
1e-2: ('c', 'centi')
})
bin_prefixes = {2**10: ('K', 'kilo'),
2**20: ('M', 'mega'),
2**30: ('G', 'mega'),
2**40: ('T', 'tera'),
2**50: ('P', 'peta'),
2**60: ('E', 'exa')
}
if mode == 'bin':
prefixes = bin_prefixes
else:
prefixes = si_prefixes
prefixes[1] = ('', '') # Unity.
# Determine appropriate multiplier.
multipliers = prefixes.keys()
multipliers.sort()
mult = None
for i in range(len(multipliers) - 1):
lower_mult = multipliers[i]
upper_mult = multipliers[i+1]
if lower_mult <= value < upper_mult:
mult_i = i
break
if mult is None:
if value < multipliers[0]:
mult_i = 0
elif value >= multipliers[-1]:
mult_i = len(multipliers) - 1
mult = multipliers[mult_i]
# Convert value for this multiplier.
new_value = value / mult
# Deal with special case due to rounding.
if sigfigs is None:
if mult_i < (len(multipliers) - 1) and \
round(new_value, dp) == \
round((multipliers[mult_i+1] / mult), dp):
mult = multipliers[mult_i + 1]
new_value = value / mult
# Concatenate multiplier symbol.
if use_full_name:
label_type = 1
else:
label_type = 0
# Round and truncate to appropriate precision.
if sigfigs is None:
str_value = eval('"%.'+str(dp)+'f" % new_value', locals(), {})
else:
str_value = eval('"%.'+str(sigfigs)+'g" % new_value', locals(), {})
return str_value + space + prefixes[mult][label_type] + suffix
def uniquify(seq, preserve_order=False):
"""Return sequence with duplicate items in sequence seq removed.
The code is based on usenet post by Tim Peters.
This code is O(N) if the sequence items are hashable, O(N**2) if not.
Peter Bengtsson has a blog post with an empirical comparison of other
approaches:
http://www.peterbe.com/plog/uniqifiers-benchmark
If order is not important and the sequence items are hashable then
list(set(seq)) is readable and efficient.
If order is important and the sequence items are hashable generator
expressions can be used (in py >= 2.4) (useful for large sequences):
seen = set()
do_something(x for x in seq if x not in seen or seen.add(x))
Arguments:
seq -- sequence
preserve_order -- if not set the order will be arbitrary
Using this option will incur a speed penalty.
(default: False)
Example showing order preservation:
>>> uniquify(['a', 'aa', 'b', 'b', 'ccc', 'ccc', 'd'], preserve_order=True)
['a', 'aa', 'b', 'ccc', 'd']
Example using a sequence of un-hashable items:
>>> uniquify([['z'], ['x'], ['y'], ['z']], preserve_order=True)
[['z'], ['x'], ['y']]
The sorted output or the non-order-preserving approach should equal
that of the sorted order-preserving approach output:
>>> unordered = uniquify([3, 3, 1, 2], preserve_order=False)
>>> unordered.sort()
>>> ordered = uniquify([3, 3, 1, 2], preserve_order=True)
>>> ordered.sort()
>>> ordered
[1, 2, 3]
>>> int(ordered == unordered)
1
"""
try:
# Attempt fast algorithm.
d = {}
if preserve_order:
# This is based on Dave Kirby's method (f8) noted in the post:
# http://www.peterbe.com/plog/uniqifiers-benchmark
return [x for x in seq if (x not in d) and not d.__setitem__(x, 0)]
else:
for x in seq:
d[x] = 0
return d.keys()
except TypeError:
# Have an unhashable object, so use slow algorithm.
result = []
app = result.append
for x in seq:
if x not in result:
app(x)
return result
# Alias to noun form for backward compatibility.
unique = uniquify
def reverse_dict(d):
"""Reverse a dictionary so the items become the keys and vice-versa.
Note: The results will be arbitrary if the items are not unique.
>>> d = reverse_dict({'a': 1, 'b': 2})
>>> d_items = d.items()
>>> d_items.sort()
>>> d_items
[(1, 'a'), (2, 'b')]
"""
result = {}
for key, value in d.items():
result[value] = key
return result
def lsb(x, n):
"""Return the n least significant bits of x.
>>> lsb(13, 3)
5
"""
return x & ((2 ** n) - 1)
def gray_encode(i):
"""Gray encode the given integer."""
return i ^ (i >> 1)
def random_vec(bits, max_value=None):
"""Generate a random binary vector of length bits and given max value."""
vector = ""
for _ in range(int(bits / 10) + 1):
i = int((2**10) * random.random())
vector += int2bin(i, 10)
if max_value and (max_value < 2 ** bits - 1):
vector = int2bin((int(vector, 2) / (2 ** bits - 1)) * max_value, bits)
return vector[0:bits]
def binary_range(bits):
"""Return a list of all possible binary numbers in order with width=bits.
It would be nice to extend it to match the
functionality of python's range() built-in function.
"""
l = []
v = ['0'] * bits
toggle = [1] + [0] * bits
while toggle[bits] != 1:
v_copy = v[:]
v_copy.reverse()
l.append(''.join(v_copy))
toggle = [1] + [0]*bits
i = 0
while i < bits and toggle[i] == 1:
if toggle[i]:
if v[i] == '0':
v[i] = '1'
toggle[i+1] = 0
else:
v[i] = '0'
toggle[i+1] = 1
i += 1
return l
def float_range(start, stop=None, step=None):
"""Return a list containing an arithmetic progression of floats.
Return a list of floats between 0.0 (or start) and stop with an
increment of step.
This is in functionality to python's range() built-in function
but can accept float increments.
As with range(), stop is omitted from the list.
"""
if stop is None:
stop = float(start)
start = 0.0
if step is None:
step = 1.0
cur = float(start)
l = []
while cur < stop:
l.append(cur)
cur += step
return l
def find_common_fixes(s1, s2):
"""Find common (prefix, suffix) of two strings.
>>> find_common_fixes('abc', 'def')
('', '')
>>> find_common_fixes('abcelephantdef', 'abccowdef')
('abc', 'def')
>>> find_common_fixes('abcelephantdef', 'abccow')
('abc', '')
>>> find_common_fixes('elephantdef', 'abccowdef')
('', 'def')
"""
prefix = []
suffix = []
i = 0
common_len = min(len(s1), len(s2))
while i < common_len:
if s1[i] != s2[i]:
break
prefix.append(s1[i])
i += 1
i = 1
while i < (common_len + 1):
if s1[-i] != s2[-i]:
break
suffix.append(s1[-i])
i += 1
suffix.reverse()
prefix = ''.join(prefix)
suffix = ''.join(suffix)
return (prefix, suffix)
def is_rotated(seq1, seq2):
"""Return true if the first sequence is a rotation of the second sequence.
>>> seq1 = ['A', 'B', 'C', 'D']
>>> seq2 = ['C', 'D', 'A', 'B']
>>> int(is_rotated(seq1, seq2))
1
>>> seq2 = ['C', 'D', 'B', 'A']
>>> int(is_rotated(seq1, seq2))
0
>>> seq1 = ['A', 'B', 'C', 'A']
>>> seq2 = ['A', 'A', 'B', 'C']
>>> int(is_rotated(seq1, seq2))
1
>>> seq2 = ['A', 'B', 'C', 'A']
>>> int(is_rotated(seq1, seq2))
1
>>> seq2 = ['A', 'A', 'C', 'B']
>>> int(is_rotated(seq1, seq2))
0
"""
# Do a sanity check.
if len(seq1) != len(seq2):
return False
# Look for occurrences of second sequence head item in first sequence.
start_indexes = []
head_item = seq2[0]
for index1 in range(len(seq1)):
if seq1[index1] == head_item:
start_indexes.append(index1)
# Check that wrapped sequence matches.
double_seq1 = seq1 + seq1
for index1 in start_indexes:
if double_seq1[index1:index1+len(seq1)] == seq2:
return True
return False
def getmodule(obj):
"""Return the module that contains the object definition of obj.
Note: Use inspect.getmodule instead.
Arguments:
obj -- python obj, generally a class or a function
Examples:
A function:
>>> module = getmodule(random.choice)
>>> module.__name__
'random'
>>> module is random
1
A class:
>>> module = getmodule(random.Random)
>>> module.__name__
'random'
>>> module is random
1
A class inheriting from a class in another module:
(note: The inheriting class must define at least one function.)
>>> class MyRandom(random.Random):
... def play(self):
... pass
>>> module = getmodule(MyRandom)
>>> if __name__ == '__main__':
... name = 'rad_util'
... else:
... name = module.__name__
>>> name
'rad_util'
>>> module is sys.modules[__name__]
1
Discussion:
This approach is slightly hackish, and won't work in various situations.
However, this was the approach recommended by GvR, so it's as good as
you'll get.
See GvR's post in this thread:
http://groups.google.com.au/group/comp.lang.python/browse_thread/thread/966a7bdee07e3b34/c3cab3f41ea84236?lnk=st&q=python+determine+class+module&rnum=4&hl=en#c3cab3f41ea84236
"""
if hasattr(obj, 'func_globals'):
func = obj
else:
# Handle classes.
func = None
for item in obj.__dict__.values():
if hasattr(item, 'func_globals'):
func = item
break
if func is None:
raise ValueError("No functions attached to object: %r" % obj)
module_name = func.func_globals['__name__']
# Get module.
module = sys.modules[module_name]
return module
def round_grid(value, grid, mode=0):
"""Round off the given value to the given grid size.
Arguments:
value -- value to be roudne
grid -- result must be a multiple of this
mode -- 0 nearest, 1 up, -1 down
Examples:
>>> round_grid(7.5, 5)
10
>>> round_grid(7.5, 5, mode=-1)
5
>>> round_grid(7.3, 5, mode=1)
10
>>> round_grid(7.3, 5.0, mode=1)
10.0
"""
off_grid = value % grid
if mode == 0:
add_one = int(off_grid >= (grid / 2.0))
elif mode == 1 and off_grid:
add_one = 1
elif mode == -1 and off_grid:
add_one = 0
result = ((int(value / grid) + add_one) * grid)
return result
def get_args(argv):
"""Store command-line args in a dictionary.
-, -- prefixes are removed
Items not prefixed with - or -- are stored as a list, indexed by 'args'
For options that take a value use --option=value
Consider using optparse or getopt (in Python standard library) instead.
"""
d = {}
args = []
for arg in argv:
if arg.startswith('-'):
parts = re.sub(r'^-+', '', arg).split('=')
if len(parts) == 2:
d[parts[0]] = parts[1]
else:
d[parts[0]] = None
else:
args.append(arg)
d['args'] = args
return d
if __name__ == '__main__':
import doctest
doctest.testmod(sys.modules['__main__'])
-392
View File
@@ -1,392 +0,0 @@
# topsort - dependency (topological) sorting and cycle finding functions
# Copyright (C) 2007 RADLogic
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# See http://www.fsf.org/licensing/licenses/lgpl.txt for full license text.
"""Provide toplogical sorting (i.e. dependency sorting) functions.
The topsort function is based on code posted on Usenet by Tim Peters.
Modifications:
- added doctests
- changed some bits to use current Python idioms
(listcomp instead of filter, +=/-=, inherit from Exception)
- added a topsort_levels version that ports items in each dependency level
into a sub-list
- added find_cycles to aid in cycle debugging
Run this module directly to run the doctests (unittests).
Make sure they all pass before checking in any modifications.
Requires Python >= 2.2
(For Python 2.2 also requires separate sets.py module)
This requires the rad_util.py module.
"""
# Provide support for Python 2.2*
from __future__ import generators
__version__ = '$Revision: 0.9 $'
__date__ = '$Date: 2007/03/27 04:15:26 $'
__credits__ = '''Tim Peters -- original topsort code
Tim Wegener -- doctesting, updating to current idioms, topsort_levels,
find_cycles
'''
# Make Python 2.3 sets look like Python 2.4 sets.
try:
set
except NameError:
from sets import Set as set
from rad_util import is_rotated
class CycleError(Exception):
"""Cycle Error"""
pass
def topsort(pairlist):
"""Topologically sort a list of (parent, child) pairs.
Return a list of the elements in dependency order (parent to child order).
>>> print topsort( [(1,2), (3,4), (5,6), (1,3), (1,5), (1,6), (2,5)] )
[1, 2, 3, 5, 4, 6]
>>> print topsort( [(1,2), (1,3), (2,4), (3,4), (5,6), (4,5)] )
[1, 2, 3, 4, 5, 6]
>>> print topsort( [(1,2), (2,3), (3,2)] )
Traceback (most recent call last):
CycleError: ([1], {2: 1, 3: 1}, {2: [3], 3: [2]})
"""
num_parents = {} # element -> # of predecessors
children = {} # element -> list of successors
for parent, child in pairlist:
# Make sure every element is a key in num_parents.
if not num_parents.has_key( parent ):
num_parents[parent] = 0
if not num_parents.has_key( child ):
num_parents[child] = 0
# Since child has a parent, increment child's num_parents count.
num_parents[child] += 1
# ... and parent gains a child.
children.setdefault(parent, []).append(child)
# Suck up everything without a parent.
answer = [x for x in num_parents.keys() if num_parents[x] == 0]
# For everything in answer, knock down the parent count on its children.
# Note that answer grows *in* the loop.
for parent in answer:
del num_parents[parent]
if children.has_key( parent ):
for child in children[parent]:
num_parents[child] -= 1
if num_parents[child] == 0:
answer.append( child )
# Following "del" isn't needed; just makes
# CycleError details easier to grasp.
del children[parent]
if num_parents:
# Everything in num_parents has at least one child ->
# there's a cycle.
raise CycleError(answer, num_parents, children)
return answer
def topsort_levels(pairlist):
"""Topologically sort a list of (parent, child) pairs into depth levels.
This returns a generator.
Turn this into a an iterator using the iter built-in function.
(if you iterate over the iterator, each element gets generated when
it is asked for, rather than generating the whole list up-front.)
Each generated element is a list of items at that dependency level.
>>> dependency_pairs = [(1,2), (3,4), (5,6), (1,3), (1,5), (1,6), (2,5)]
>>> for level in iter(topsort_levels( dependency_pairs )):
... print level
[1]
[2, 3]
[4, 5]
[6]
>>> dependency_pairs = [(1,2), (1,3), (2,4), (3,4), (5,6), (4,5)]
>>> for level in iter(topsort_levels( dependency_pairs )):
... print level
[1]
[2, 3]
[4]
[5]
[6]
>>> dependency_pairs = [(1,2), (2,3), (3,4), (4, 3)]
>>> try:
... for level in iter(topsort_levels( dependency_pairs )):
... print level
... except CycleError, exc:
... print 'CycleError:', exc
[1]
[2]
CycleError: ({3: 1, 4: 1}, {3: [4], 4: [3]})
The cycle error should look like.
CycleError: ({3: 1, 4: 1}, {3: [4], 4: [3]})
# todo: Make the doctest more robust (i.e. handle arbitrary dict order).
"""
num_parents = {} # element -> # of predecessors
children = {} # element -> list of successors
for parent, child in pairlist:
# Make sure every element is a key in num_parents.
if not num_parents.has_key( parent ):
num_parents[parent] = 0
if not num_parents.has_key( child ):
num_parents[child] = 0
# Since child has a parent, increment child's num_parents count.
num_parents[child] += 1
# ... and parent gains a child.
children.setdefault(parent, []).append(child)
return topsort_levels_core(num_parents, children)
def topsort_levels_core(num_parents, children):
"""Topologically sort a bunch of interdependent items based on dependency.
This returns a generator.
Turn this into a an iterator using the iter built-in function.
(if you iterate over the iterator, each element gets generated when
it is asked for, rather than generating the whole list up-front.)
Each generated element is a list of items at that dependency level.
>>> list(topsort_levels_core(
... {1: 0, 2: 1, 3: 1, 4: 1, 5: 2, 6: 2},
... {1: [2, 3, 5, 6], 2: [5], 3: [4], 4: [], 5: [6]}))
[[1], [2, 3], [4, 5], [6]]
>>> list(topsort_levels_core(
... {1: 0, 2: 2, 3: 1},
... {1: [2], 2: [3], 3: [2]}))
Traceback (most recent call last):
CycleError: ({2: 1, 3: 1}, {2: [3], 3: [2]})
This function has a more complicated interface than topsort_levels,
but is useful if the data is easier to generate in this form.
Arguments:
num_parents -- key: item, value: number of parents (predecessors)
children -- key: item, value: list of children (successors)
"""
while 1:
# Suck up everything without a predecessor.
level_parents = [x for x in num_parents.keys() if num_parents[x] == 0]
if not level_parents:
break
# Offer the next generated item,
# which is a list of the items at this dependency level.
yield level_parents
# For everything item in this level,
# decrement the parent count,
# since we have accounted for its parent.
for level_parent in level_parents:
del num_parents[level_parent]
if children.has_key(level_parent):
for level_parent_child in children[level_parent]:
num_parents[level_parent_child] -= 1
del children[level_parent]
if num_parents:
# Everything in num_parents has at least one child ->
# there's a cycle.
raise CycleError(num_parents, children)
else:
# This is the end of the generator.
raise StopIteration
def find_cycles(parent_children):
"""Yield cycles. Each result is a list of items comprising a cycle.
Use a 'stack' based approach to find all the cycles.
This is a generator, so yields each cycle as it finds it.
It is implicit that the last item in each cycle list is a parent of the
first item (thereby forming a cycle).
Arguments:
parent_children -- parent -> collection of children
Simplest cycle:
>>> cycles = list(find_cycles({'A': ['B'], 'B': ['A']}))
>>> len(cycles)
1
>>> cycle = cycles[0]
>>> cycle.sort()
>>> print cycle
['A', 'B']
Simplest cycle with extra baggage at the start and the end:
>>> cycles = list(find_cycles(parent_children={'A': ['B'],
... 'B': ['C'],
... 'C': ['B', 'D'],
... 'D': [],
... }))
>>> len(cycles)
1
>>> cycle = cycles[0]
>>> cycle.sort()
>>> print cycle
['B', 'C']
Double cycle:
>>> cycles = list(find_cycles(parent_children={'A': ['B'],
... 'B': ['C1', 'C2'],
... 'C1': ['D1'],
... 'D1': ['E1'],
... 'E1': ['D1'],
... 'C2': ['D2'],
... 'D2': ['E2'],
... 'E2': ['D2'],
... }))
>>> len(cycles)
2
>>> for cycle in cycles:
... cycle.sort()
>>> cycles.sort()
>>> cycle1 = cycles[0]
>>> cycle1.sort()
>>> print cycle1
['D1', 'E1']
>>> cycle2 = cycles[1]
>>> cycle2.sort()
>>> print cycle2
['D2', 'E2']
Simple cycle with children not specified for one item:
# todo: Should this barf instead?
>>> cycles = list(find_cycles(parent_children={'A': ['B'],
... 'B': ['A'],
... 'C': ['D']}))
>>> len(cycles)
1
>>> cycle = cycles[0]
>>> cycle.sort()
>>> print cycle
['A', 'B']
Diamond cycle
>>> cycles = list(find_cycles(parent_children={'A': ['B1', 'B2'],
... 'B1': ['C'],
... 'B2': ['C'],
... 'C': ['A', 'B1']}))
>>> len(cycles)
3
>>> sorted_cycles = []
>>> for cycle in cycles:
... cycle = list(cycle)
... cycle.sort()
... sorted_cycles.append(cycle)
>>> sorted_cycles.sort()
>>> for cycle in sorted_cycles:
... print cycle
['A', 'B1', 'C']
['A', 'B2', 'C']
['B1', 'C']
Hairy case (order can matter if something is wrong):
(Note order of B and C in the list.)
>>> cycles = list(find_cycles(parent_children={
... 'TD': ['DD'],
... 'TC': ['DC'],
... 'DC': ['DQ'],
... 'C': ['DQ'],
... 'DQ': ['IA', 'TO'],
... 'IA': ['A'],
... 'A': ['B', 'C'],
... }))
>>> len(cycles)
1
>>> cycle = cycles[0]
>>> cycle.sort()
>>> print cycle
['A', 'C', 'DQ', 'IA']
"""
cycles = []
visited_nodes = set()
for parent in parent_children:
if parent in visited_nodes:
# This node is part of a path that has already been traversed.
continue
paths = [[parent]]
while paths:
path = paths.pop()
parent = path[-1]
try:
children = parent_children[parent]
except KeyError:
continue
for child in children:
# Keeping a set of the path nodes, for O(1) lookups at the
# expense of more memory and complexity, actually makes speed
# worse. (Due to construction of sets.)
# This is O(N).
if child in path:
# This is a cycle.
cycle = path[path.index(child):]
# Check that this is not a dup cycle.
is_dup = False
for other_cycle in cycles:
if is_rotated(other_cycle, cycle):
is_dup = True
break
if not is_dup:
cycles.append(cycle)
yield cycle
else:
# Push this new path onto the 'stack'.
# This is probably the most expensive part of the algorithm
# (a list copy).
paths.append(path + [child])
# Mark the node as visited.
visited_nodes.add(child)
if __name__ == '__main__':
# Run the doctest tests.
import sys
import doctest
doctest.testmod(sys.modules['__main__'])
-502
View File
@@ -1,502 +0,0 @@
## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
import types
import re
import os
import subprocess
import shutil
import sys
from waflib import Task, Options, Configure, TaskGen, Logs, Build, Utils, Errors
from waflib.Errors import WafError
# feature = TaskGen.feature
# after = TaskGen.after
# https://github.com/named-data-ndnSIM/pybindgen
REQUIRED_PYBINDGEN_VERSION = '0.17.0.post45+ng4806e4f'
REQUIRED_PYGCCXML_VERSION = (0, 9, 5)
RUN_ME=-3
def add_to_python_path(path):
if os.environ.get('PYTHONPATH', ''):
os.environ['PYTHONPATH'] = path + os.pathsep + os.environ.get('PYTHONPATH')
else:
os.environ['PYTHONPATH'] = path
def set_pybindgen_pythonpath(env):
if env['WITH_PYBINDGEN']:
add_to_python_path(env['WITH_PYBINDGEN'])
def options(opt):
opt.load('python')
opt.add_option('--disable-python',
help=("Don't build Python bindings."),
action="store_true", default=False,
dest='python_disable')
opt.add_option('--apiscan',
help=("Rescan the API for the indicated module(s), for Python bindings. "
"Needs working GCCXML / pygccxml environment. "
"The metamodule 'all' expands to all available ns-3 modules."),
default=None, dest='apiscan', metavar="MODULE[,MODULE...]")
opt.add_option('--with-pybindgen',
help=('Path to an existing pybindgen source tree to use.'),
default=None,
dest='with_pybindgen', type="string")
opt.add_option('--with-python',
help=('Path to the Python interpreter to use.'),
default=None, dest='with_python', type="string")
opt.add_option('--no32bit-scan',
help=("Don't scan for the 32-bit variant of the bindings on 64-bit platforms."),
action="store_true", default=False,
dest='no32bit_scan')
def configure(conf):
conf.env['ENABLE_PYTHON_BINDINGS'] = False
if Options.options.python_disable:
conf.report_optional_feature("python", "Python Bindings", False,
"disabled by user request")
return
# Disable python in static builds (bug #1253)
if ((conf.env['ENABLE_STATIC_NS3']) or \
(conf.env['ENABLE_SHARED_AND_STATIC_NS3'])):
conf.report_optional_feature("python", "Python Bindings", False,
"bindings incompatible with static build")
return
enabled_modules = list(conf.env['NS3_ENABLED_MODULES'])
enabled_modules.sort()
available_modules = list(conf.env['NS3_MODULES'])
available_modules.sort()
all_modules_enabled = (enabled_modules == available_modules)
conf.load('misc', tooldir=['waf-tools'])
if sys.platform == 'cygwin':
conf.report_optional_feature("python", "Python Bindings", False,
"unsupported platform 'cygwin'")
Logs.warn("Python is not supported in CygWin environment. Try MingW instead.")
return
## Check for Python
if Options.options.with_python is not None:
conf.env.PYTHON = Options.options.with_python
try:
conf.load('python')
except Errors.ConfigurationError, ex:
conf.report_optional_feature("python", "Python Bindings", False,
"The python interpreter was not found")
return
try:
conf.check_python_version((2,3))
except Errors.ConfigurationError, ex:
conf.report_optional_feature("python", "Python Bindings", False,
"The python found version is too low (2.3 required)")
return
try:
conf.check_python_headers()
except Errors.ConfigurationError, ex:
conf.report_optional_feature("python", "Python Bindings", False,
"Python library or headers missing")
return
# stupid Mac OSX Python wants to build extensions as "universal
# binaries", i386, x86_64, and ppc, but this way the type
# __uint128_t is not available. We need to disable the multiarch
# crap by removing the -arch parameters.
for flags_var in ["CFLAGS_PYEXT", "CFLAGS_PYEMBED", "CXXFLAGS_PYEMBED",
"CXXFLAGS_PYEXT", "LINKFLAGS_PYEMBED", "LINKFLAGS_PYEXT"]:
flags = conf.env[flags_var]
i = 0
while i < len(flags):
if flags[i] == '-arch':
del flags[i]
del flags[i]
continue
i += 1
conf.env[flags_var] = flags
# -fvisibility=hidden optimization
if (conf.env['CXX_NAME'] == 'gcc' and [int(x) for x in conf.env['CC_VERSION']] >= [4,0,0]
and conf.check_compilation_flag('-fvisibility=hidden')):
conf.env.append_value('CXXFLAGS_PYEXT', '-fvisibility=hidden')
conf.env.append_value('CCFLAGS_PYEXT', '-fvisibility=hidden')
if conf.check_compilation_flag('-Wno-array-bounds'):
conf.env.append_value('CXXFLAGS_PYEXT', '-Wno-array-bounds')
# Check for the location of pybindgen
if Options.options.with_pybindgen is not None:
if os.path.isdir(Options.options.with_pybindgen):
conf.msg("Checking for pybindgen location", ("%s (given)" % Options.options.with_pybindgen))
conf.env['WITH_PYBINDGEN'] = os.path.abspath(Options.options.with_pybindgen)
else:
# ns-3-dev uses ../pybindgen, while ns-3 releases use ../REQUIRED_PYBINDGEN_VERSION
pybindgen_dir = os.path.join('..', "pybindgen")
pybindgen_release_str = "pybindgen-" + '.'.join([str(x) for x in REQUIRED_PYBINDGEN_VERSION])
pybindgen_release_dir = os.path.join('..', pybindgen_release_str)
if os.path.isdir(pybindgen_dir):
conf.msg("Checking for pybindgen location", ("%s (guessed)" % pybindgen_dir))
conf.env['WITH_PYBINDGEN'] = os.path.abspath(pybindgen_dir)
elif os.path.isdir(pybindgen_release_dir):
conf.msg("Checking for pybindgen location", ("%s (guessed)" % pybindgen_release_dir))
conf.env['WITH_PYBINDGEN'] = os.path.abspath(pybindgen_release_dir)
del pybindgen_dir
del pybindgen_release_dir
if not conf.env['WITH_PYBINDGEN']:
conf.msg("Checking for pybindgen location", False)
# Check for pybindgen
set_pybindgen_pythonpath(conf.env)
try:
conf.check_python_module('pybindgen')
except Errors.ConfigurationError:
Logs.warn("pybindgen missing => no python bindings")
conf.report_optional_feature("python", "Python Bindings", False,
"PyBindGen missing")
return
else:
def getVersion():
out = subprocess.Popen([conf.env['PYTHON'][0], "-c",
"try:\n"
" import pybindgen.version\n"
" print(pybindgen.__version__)\n"
"except:\n"
" pass"],
stdout=subprocess.PIPE).communicate()[0]
return out.strip()
pybindgen_version = getVersion()
if pybindgen_version == '': # version is not generated yet (e.g., cloned from git repo directly)
subprocess.Popen([conf.env['PYTHON'][0], "setup.py", "--version"],
cwd=conf.env['WITH_PYBINDGEN'],
stdout=subprocess.PIPE).communicate()[0]
pybindgen_version = getVersion()
conf.msg('Checking for pybindgen version', pybindgen_version)
if not (pybindgen_version == REQUIRED_PYBINDGEN_VERSION):
Logs.warn("pybindgen (found %r), (need %r)" %
(pybindgen_version, REQUIRED_PYBINDGEN_VERSION))
conf.report_optional_feature("python", "Python Bindings", False,
"PyBindGen version not correct and newer version could not be retrieved")
return
def test(t1, t2):
test_program = '''
#include <stdint.h>
#include <vector>
int main ()
{
std::vector< %(type1)s > t = std::vector< %(type2)s > ();
return 0;
}
''' % dict(type1=t1, type2=t2)
try:
ret = conf.check(compiler='cxx', fragment=test_program, features='cxx')
except Errors.ConfigurationError:
ret = False
conf.msg('Checking for types %s and %s equivalence' % (t1, t2), (ret and 'no' or 'yes'))
return ret
uint64_is_long = test("uint64_t", "unsigned long")
uint64_is_long_long = test("uint64_t", "unsigned long long")
if uint64_is_long:
conf.env['PYTHON_BINDINGS_APIDEFS'] = 'gcc-LP64'
elif uint64_is_long_long:
conf.env['PYTHON_BINDINGS_APIDEFS'] = 'gcc-ILP32'
else:
conf.env['PYTHON_BINDINGS_APIDEFS'] = None
if conf.env['PYTHON_BINDINGS_APIDEFS'] is None:
msg = 'none available'
else:
msg = conf.env['PYTHON_BINDINGS_APIDEFS']
conf.msg('Checking for the apidefs that can be used for Python bindings', msg)
if conf.env['PYTHON_BINDINGS_APIDEFS'] is None:
conf.report_optional_feature("python", "Python Bindings", False,
"No apidefs are available that can be used in this system")
return
## If all has gone well, we finally enable the Python bindings
conf.env['ENABLE_PYTHON_BINDINGS'] = True
conf.report_optional_feature("python", "Python Bindings", True, None)
# check cxxabi stuff (which Mac OS X Lion breaks)
fragment = r"""
# include <cxxabi.h>
int main ()
{
const abi::__si_class_type_info *_typeinfo __attribute__((unused)) = NULL;
return 0;
}
"""
gcc_rtti_abi = conf.check_nonfatal(fragment=fragment, msg="Checking for internal GCC cxxabi",
okmsg="complete", errmsg='incomplete',
mandatory=False)
conf.env["GCC_RTTI_ABI_COMPLETE"] = str(bool(gcc_rtti_abi))
## Check for pygccxml
try:
conf.check_python_module('pygccxml')
except Errors.ConfigurationError:
conf.report_optional_feature("pygccxml", "Python API Scanning Support", False,
"Missing 'pygccxml' Python module")
return
out = subprocess.Popen([conf.env['PYTHON'][0], "-c",
"import pygccxml; print pygccxml.__version__"],
stdout=subprocess.PIPE).communicate()[0]
pygccxml_version_str = out.strip()
# Bug 2013: pygccxml versions > 1.0.0 prepend a 'v' to version number
pygccxml_version_str = pygccxml_version_str.lstrip('v')
pygccxml_version = tuple([int(x) for x in pygccxml_version_str.split('.')])
conf.msg('Checking for pygccxml version', pygccxml_version_str)
if not (pygccxml_version >= REQUIRED_PYGCCXML_VERSION):
Logs.warn("pygccxml (found %s) is too old (need %s) => "
"automatic scanning of API definitions will not be possible" %
(pygccxml_version_str,
'.'.join([str(x) for x in REQUIRED_PYGCCXML_VERSION])))
conf.report_optional_feature("pygccxml", "Python API Scanning Support", False,
"pygccxml too old")
return
## Check gccxml version
try:
gccxml = conf.find_program('gccxml', var='GCCXML')
except WafError:
gccxml = None
if not gccxml:
Logs.warn("gccxml missing; automatic scanning of API definitions will not be possible")
conf.report_optional_feature("pygccxml", "Python API Scanning Support", False,
"gccxml missing")
return
gccxml_version_line = os.popen(gccxml[0] + " --version").readline().strip()
m = re.match( "^GCC-XML version (\d\.\d(\.\d)?)$", gccxml_version_line)
try:
gccxml_version = m.group(1)
gccxml_version_ok = ([int(s) for s in gccxml_version.split('.')] >= [0, 9])
except AttributeError:
gccxml_version = gccxml_version_line
gccxml_version_ok = False
conf.msg('Checking for gccxml version', gccxml_version)
if not gccxml_version_ok:
Logs.warn("gccxml version unknown or too old, need version >= 0.9; automatic scanning of API definitions will not be possible")
conf.report_optional_feature("pygccxml", "Python API Scanning Support", False,
"gccxml too old")
return
## If we reached
conf.env['ENABLE_PYTHON_SCANNING'] = True
conf.report_optional_feature("pygccxml", "Python API Scanning Support", True, None)
# ---------------------
def get_headers_map(bld):
headers_map = {} # header => module
for ns3headers in bld.all_task_gen:
if 'ns3header' in getattr(ns3headers, "features", []):
if ns3headers.module.endswith('-test'):
continue
for h in ns3headers.to_list(ns3headers.headers):
headers_map[os.path.basename(h)] = ns3headers.module
return headers_map
def get_module_path(bld, module):
for ns3headers in bld.all_task_gen:
if 'ns3header' in getattr(ns3headers, "features", []):
if ns3headers.module == module:
break
else:
raise ValueError("Module %r not found" % module)
return ns3headers.path.abspath()
class apiscan_task(Task.Task):
"""Uses gccxml to scan the file 'everything.h' and extract API definitions.
"""
after = 'gen_ns3_module_header ns3header'
before = 'cxx command'
color = "BLUE"
def __init__(self, curdirnode, env, bld, target, cflags, module):
self.bld = bld
super(apiscan_task, self).__init__(generator=self, env=env)
self.curdirnode = curdirnode
self.env = env
self.target = target
self.cflags = cflags
self.module = module
def display(self):
return 'api-scan-%s\n' % (self.target,)
def uid(self):
try:
return self.uid_
except AttributeError:
m = Utils.md5()
up = m.update
up(self.__class__.__name__.encode())
up(self.curdirnode.abspath().encode())
up(self.target)
self.uid_ = m.digest()
return self.uid_
def run(self):
self.inputs = [self.bld.bldnode.find_resource("ns3/{0}-module.h".format(self.module))]
self.outputs = [self.bld.srcnode.find_resource("src/{}/bindings/modulegen__{}.py".format(self.module, self.target))]
top_builddir = self.bld.bldnode.abspath()
module_path = get_module_path(self.bld, self.module)
headers_map = get_headers_map(self.bld)
scan_header = os.path.join(top_builddir, "ns3", "%s-module.h" % self.module)
if not os.path.exists(scan_header):
Logs.error("Cannot apiscan module %r: %s does not exist" % (self.module, scan_header))
return 0
argv = [
self.env['PYTHON'][0],
os.path.join(self.curdirnode.abspath(), 'ns3modulescan-modular.py'), # scanning script
top_builddir,
self.module,
repr(get_headers_map(self.bld)),
os.path.join(module_path, "bindings", 'modulegen__%s.py' % (self.target)), # output file
self.cflags,
]
scan = subprocess.Popen(argv, stdin=subprocess.PIPE)
retval = scan.wait()
return retval
def runnable_status(self):
# By default, Waf Task will skip running a task if the signature of
# the build has not changed. We want this task to always run if
# invoked by the user, particularly since --apiscan=all will require
# invoking this task many times, once per module.
return RUN_ME
def get_modules_and_headers(bld):
"""
Gets a dict of
module_name => ([module_dep1, module_dep2, ...], [module_header1, module_header2, ...])
tuples, one for each module.
"""
retval = {}
for module in bld.all_task_gen:
if not module.name.startswith('ns3-'):
continue
if module.name.endswith('-test'):
continue
module_name = module.name[4:] # strip the ns3- prefix
## find the headers object for this module
headers = []
for ns3headers in bld.all_task_gen:
if 'ns3header' not in getattr(ns3headers, "features", []):
continue
if ns3headers.module != module_name:
continue
for source in ns3headers.to_list(ns3headers.headers):
headers.append(os.path.basename(source))
retval[module_name] = (list(module.module_deps), headers)
return retval
class gen_ns3_compat_pymod_task(Task.Task):
"""Generates a 'ns3.py' compatibility module."""
before = 'cxx'
color = 'BLUE'
def run(self):
assert len(self.outputs) == 1
outfile = file(self.outputs[0].abspath(), "w")
print >> outfile, "import warnings"
print >> outfile, 'warnings.warn("the ns3 module is a compatibility layer '\
'and should not be used in newly written code", DeprecationWarning, stacklevel=2)'
print >> outfile
for module in self.bld.env['PYTHON_MODULES_BUILT']:
print >> outfile, "from ns.%s import *" % (module.replace('-', '_'))
outfile.close()
return 0
def build(bld):
if Options.options.python_disable:
return
env = bld.env
set_pybindgen_pythonpath(env)
if Options.options.apiscan:
if not env['ENABLE_PYTHON_SCANNING']:
raise WafError("Cannot re-scan python bindings: (py)gccxml not available")
scan_targets = []
if sys.platform == 'cygwin':
scan_targets.append(('gcc_cygwin', ''))
else:
import struct
if struct.calcsize('I') == 4 and struct.calcsize('L') == 8 and struct.calcsize('P') == 8:
if not Options.options.no32bit_scan:
scan_targets.append(('gcc_ILP32', '-m32'))
scan_targets.append(('gcc_LP64', '-m64'))
elif struct.calcsize('I') == 4 and struct.calcsize('L') == 4 and struct.calcsize('P') == 4:
scan_targets.append(('gcc_ILP32', ''))
else:
raise WafError("Cannot scan python bindings for unsupported data model")
test_module_path = bld.path.find_dir("../../src/test")
if Options.options.apiscan == 'all':
scan_modules = []
for mod in bld.all_task_gen:
if not mod.name.startswith('ns3-'):
continue
if mod.path.is_child_of(test_module_path):
continue
if mod.name.endswith('-test'):
continue
bindings_enabled = (mod.name in env.MODULAR_BINDINGS_MODULES)
#print mod.name, bindings_enabled
if bindings_enabled:
scan_modules.append(mod.name.split('ns3-')[1])
else:
scan_modules = Options.options.apiscan.split(',')
print "Modules to scan: ", scan_modules
for target, cflags in scan_targets:
group = bld.get_group(bld.current_group)
for module in scan_modules:
group.append(apiscan_task(bld.path, env, bld, target, cflags, module))
return
if env['ENABLE_PYTHON_BINDINGS']:
task = gen_ns3_compat_pymod_task(env=env.derive())
task.set_outputs(bld.path.find_or_declare("ns3.py"))
task.dep_vars = ['PYTHON_MODULES_BUILT']
task.bld = bld
grp = bld.get_group(bld.current_group)
grp.append(task)
bld(features='copy', source="ns__init__.py", target='ns/__init__.py')
bld.install_as('${PYTHONARCHDIR}/ns/__init__.py', 'ns__init__.py')
# note: the actual build commands for the python bindings are in
# src/wscript, not here.
+561
View File
@@ -0,0 +1,561 @@
## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
# Copyright (c) 2006 INRIA
# All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation;
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
import os
import os.path
import shutil
from SCons.Environment import Environment
from SCons.Builder import Builder
from SCons.Action import Action
import SCons
# hack stolen from wengo
# to get an ARGUMENTS defined correctly
try:
ARGUMENTS = SCons.Script.ARGUMENTS
COMMAND_LINE_TARGETS = SCons.Script.COMMAND_LINE_TARGETS
except AttributeError:
from SCons.Script.SConscript import Arguments
from SCons.Script.SConscript import CommandLineTargets
ARGUMENTS = Arguments
COMMAND_LINE_TARGETS = CommandLineTargets
class Ns3Module:
def __init__(self, name, dir):
self.sources = []
self.inst_headers = []
self.headers = []
self.extra_dist = []
self.deps = []
self.external_deps = []
self.config = []
self.name = name
self.dir = dir
self.executable = False
self.library = True
self.ldflags = []
self.header_inst_dir = ''
def set_library(self):
self.library = True
self.executable = False
def set_executable(self):
self.library = False
self.executable = True
def add_config(self, config_fn):
self.config.append(config_fn)
def add_extra_dist(self, dist):
self.extra_dist.append(dist)
def add_external_dep(self, dep):
self.external_deps.append(dep)
def add_dep(self, dep):
self.deps.append(dep)
def add_deps(self, deps):
self.deps.extend(deps)
def add_source(self, source):
self.sources.append(source)
def add_sources(self, sources):
self.sources.extend(sources)
def add_header(self, header):
self.headers.append(header)
def add_headers(self, headers):
self.headers.extend(headers)
def add_inst_header(self, header):
self.inst_headers.append(header)
def add_inst_headers(self, headers):
self.inst_headers.extend(headers)
def add_ldflags (self, ldflags):
self.ldflags.extend (ldflags)
def add_ldflag (self, ldflag):
self.add_ldflags ([ldflag])
def set_header_inst_dir (self, inst_dir):
self.header_inst_dir = inst_dir
def MyCopyAction(target, source, env):
try:
if len(target) == len(source):
for i in range(len(target)):
shutil.copy(source[i].path, target[i].path)
return 0
else:
return 'invalid target/source match'
except:
print
return 'exception'
def MyCopyActionPrint(target, source, env):
if len(target) == len(source):
output = ''
for i in range(len(target)):
output = output + 'copy \'' + source[i].path + '\' to \'' + target[i].path + '\''
if i < len(target) - 1:
output = output + '\n'
return output
else:
return 'error in copy'
def GcxxEmitter(target, source, env):
if os.path.exists(source[0].path):
return [target, source]
else:
return [[], []]
def MyRmTree(target, source, env):
shutil.rmtree(env['RM_DIR'])
return 0
def MyRmTreePrint(target, source, env):
return ''
def print_cmd_line(s, target, src, env):
print 'Building ' + (' and '.join([str(x) for x in target])) + '...'
class Ns3BuildVariant:
def __init__(self):
self.static = False
self.gcxx_deps = False
self.gcxx_root = ''
self.build_root = ''
self.env = None
class Ns3:
def __init__(self):
self.__modules = []
self.extra_dist = []
self.build_dir = 'build'
self.version = '0.0.1'
self.name = 'noname'
self.distname = 'noname'
self.doxygen_config = ''
def add(self, module):
self.__modules.append(module)
def add_extra_dist(self, dist):
self.extra_dist.append(dist)
def __get_module(self, name):
for module in self.__modules:
if module.name == name:
return module
return None
def get_mod_output(self, module, variant):
if module.executable:
suffix = variant.env.subst(variant.env['PROGSUFFIX'])
filename = os.path.join(variant.build_root, 'bin',
module.name + suffix)
else:
if variant.static:
prefix = variant.env['LIBPREFIX']
suffix = variant.env['LIBSUFFIX']
else:
prefix = variant.env['SHLIBPREFIX']
suffix = variant.env['SHLIBSUFFIX']
prefix = variant.env.subst(prefix)
suffix = variant.env.subst(suffix)
filename = os.path.join(variant.build_root, 'lib',
prefix + module.name + suffix)
return filename
def get_obj_builders(self, variant, module):
env = variant.env.Copy ()
objects = []
hash = {}
self.get_internal_deps (module, hash)
for dep in hash.values ():
if dep.header_inst_dir != '':
inc_dir = os.path.join(variant.build_root, 'include',
self.name, dep.header_inst_dir)
env.Append (CPPPATH = [inc_dir])
if len(module.config) > 0:
src_config_file = os.path.join(self.build_dir, 'config', module.name + '-config.h')
tgt_config_file = os.path.join(variant.build_root, 'include',
self.name, module.name + '-config.h')
for source in module.sources:
obj_file = os.path.splitext(source)[0] + '.o'
tgt = os.path.join(variant.build_root, module.dir, obj_file)
src = os.path.join(module.dir, source)
if variant.static:
obj_builder = env.StaticObject(target = tgt, source = src)
else:
obj_builder = env.SharedObject(target = tgt, source = src)
if len(module.config) > 0:
config_file = env.MyCopyBuilder(target = [tgt_config_file],
source = [src_config_file])
env.Depends(obj_builder, config_file)
if variant.gcxx_deps:
gcno_tgt = os.path.join(variant.build_root, module.dir,
os.path.splitext(source)[0] + '.gcno')
gcda_tgt = os.path.join(variant.build_root, module.dir,
os.path.splitext(source)[0] + '.gcda')
gcda_src = os.path.join(variant.gcxx_root, module.dir,
os.path.splitext(source)[0] + '.gcda')
gcno_src = os.path.join(variant.gcxx_root, module.dir,
os.path.splitext(source)[0] + '.gcno')
gcno_builder = env.CopyGcxxBuilder(target = gcno_tgt, source = gcno_src)
gcda_builder = env.CopyGcxxBuilder(target = gcda_tgt, source = gcda_src)
env.Depends(obj_builder, gcda_builder)
env.Depends(obj_builder, gcno_builder)
objects.append(obj_builder)
return objects
def get_internal_deps(self, module, hash):
for dep_name in module.deps:
dep = self.__get_module(dep_name)
hash[dep_name] = dep
self.get_internal_deps(dep, hash)
def get_external_deps(self, module):
hash = {}
self.get_internal_deps(module, hash)
ext_hash = {}
for mod in hash.values():
for ext_dep in mod.external_deps:
ext_hash[ext_dep] = 1
return ext_hash.keys()
def get_sorted_deps(self, module):
h = {}
self.get_internal_deps(module, h)
modules = []
for dep in h.keys():
deps_copy = []
mod = h[dep]
deps_copy.extend(mod.deps)
modules.append([mod, deps_copy])
sorted = []
while len(modules) > 0:
to_remove = []
for item in modules:
if len(item[1]) == 0:
to_remove.append(item[0].name)
for item in to_remove:
for i in modules:
if item in i[1]:
i[1].remove(item)
new_modules = []
for mod in modules:
found = False
for i in to_remove:
if i == mod[0].name:
found = True
break
if not found:
new_modules.append(mod)
modules = new_modules
sorted.extend(to_remove)
sorted.reverse()
# append external deps
ext_deps = self.get_external_deps(module)
for dep in ext_deps:
sorted.append(dep)
return sorted
def gen_mod_dep(self, variant):
build_root = variant.build_root
cpp_path = os.path.join(variant.build_root, 'include')
env = variant.env
env.Append(CPPPATH = [cpp_path])
header_dir = os.path.join(build_root, 'include', self.name)
lib_path = os.path.join(build_root, 'lib')
env.Append (LIBPATH = [lib_path])
module_builders = []
for module in self.__modules:
my_env = env.Copy ();
objects = self.get_obj_builders(variant, module)
libs = self.get_sorted_deps(module)
my_env.Append (LIBS = libs)
my_env.Append (LINKFLAGS = module.ldflags)
filename = self.get_mod_output(module, variant)
if module.executable:
module_builder = my_env.Program(target=filename, source=objects)
else:
if variant.static:
module_builder = my_env.StaticLibrary(target=filename, source=objects)
else:
module_builder = my_env.SharedLibrary(target=filename, source=objects)
for dep_name in module.deps:
dep = self.__get_module(dep_name)
my_env.Depends(module_builder, self.get_mod_output(dep, variant))
for header in module.inst_headers:
if module.header_inst_dir != '':
tgt = os.path.join(header_dir, module.header_inst_dir, header)
else:
tgt = os.path.join(header_dir, header)
src = os.path.join(module.dir, header)
#builder = env.Install(target = tgt, source = src)
header_builder = my_env.MyCopyBuilder(target=tgt, source=src)
my_env.Depends(module_builder, header_builder)
module_builders.append(module_builder)
return module_builders
def gen_mod_config(self, env):
config_dir = os.path.join(self.build_dir, 'config')
for module in self.__modules:
if len(module.config) > 0:
config_file = os.path.join(config_dir, module.name + '-config.h')
config_file_guard = module.name + '_CONFIG_H'
config_file_guard.upper()
if not os.path.isfile(config_file):
if not os.path.isdir(config_dir):
os.makedirs(config_dir)
outfile = open(config_file, 'w')
outfile.write('#ifndef ' + config_file_guard + '\n')
outfile.write('#define ' + config_file_guard + '\n')
config = env.Configure()
for fn in module.config:
output = fn(env, config)
for o in output:
outfile.write(o)
outfile.write('\n')
outfile.write('#endif /*' + config_file_guard + '*/\n')
config.Finish()
def generate_dependencies(self):
inheritenv = (ARGUMENTS.get('inheritenv', 'n') in 'yY1')
if inheritenv:
env = Environment(ENV=os.environ)
else:
env = Environment()
self.gen_mod_config(env)
cc = env['CC']
cxx = env.subst(env['CXX'])
if cc == '':
print "Missing C compiler."
env.Exit (1);
if cxx == '':
print "Missing C++ compiler."
env.Exit (1);
common_flags = ARGUMENTS.get('cflags', '').split(' ')
cxxflags = ARGUMENTS.get('cxxflags', '').split(' ')
ldflags = ARGUMENTS.get('ldflags', '').split(' ')
if cc == 'cl' and cxx == 'cl':
env = Environment(tools=['mingw'])
cc = env['CC']
cxx = env.subst(env['CXX'])
if cc == 'gcc' and cxx == 'g++':
common_flags.extend(['-g3', '-Wall', '-Werror'])
debug_flags = []
opti_flags = ['-O3']
elif cc == 'cl' and cxx == 'cl':
env = Environment(ENV=os.environ)
debug_flags = ['-W1', '-GX', '-EHsc', '-D_DEBUG', '/MDd']
opti_flags = ['-O2', '-EHsc', '-DNDEBUG', '/MD']
cc = ARGUMENTS.get ('cc', '')
cxx = ARGUMENTS.get ('cxx', '')
if cc != '':
env.Replace (CC = cc)
if cxx != '':
env.Replace (CXX = cxx)
env.Append(CCFLAGS = common_flags,
CPPDEFINES = ['RUN_SELF_TESTS'],
TARFLAGS = '-c -z',
CPPFLAGS = cxxflags,
LINKFLAGS = ldflags)
if env['PLATFORM'] == 'posix':
env.Append(LINKFLAGS = ' -z origin')
env.Append(RPATH=env.Literal(os.path.join('\\$$ORIGIN', os.pardir, 'lib')))
verbose = ARGUMENTS.get('verbose', 'n')
if verbose == 'n':
env['PRINT_CMD_LINE_FUNC'] = print_cmd_line
header_builder = Builder(action = Action(MyCopyAction, strfunction=MyCopyActionPrint))
env.Append(BUILDERS = {'MyCopyBuilder':header_builder})
gcxx_builder = Builder(action = Action(MyCopyAction, strfunction=MyCopyActionPrint),
emitter = GcxxEmitter)
env.Append(BUILDERS = {'CopyGcxxBuilder':gcxx_builder})
variant = Ns3BuildVariant()
builders = []
gcov_env = env.Copy()
gcov_env.Append(CFLAGS = ['-fprofile-arcs', '-ftest-coverage'],
CXXFLAGS = ['-fprofile-arcs', '-ftest-coverage'],
LINKFLAGS = ['-fprofile-arcs'])
# code coverage analysis
variant.static = False
variant.env = gcov_env
variant.build_root = os.path.join(self.build_dir, 'gcov')
builders = self.gen_mod_dep(variant)
for builder in builders:
gcov_env.Alias('gcov', builder)
gcov_env.Alias('lcov-report')
if 'lcov-report' in COMMAND_LINE_TARGETS:
lcov_report_dir = os.path.join(self.build_dir, 'lcov-report')
create_dir_command = "rm -rf " + lcov_report_dir
create_dir_command += " && mkdir " + lcov_report_dir + ";"
gcov_env.Execute(create_dir_command)
info_file = os.path.join(lcov_report_dir, self.name + '.info')
lcov_command = "utils/lcov/lcov -c -d . -o " + info_file
lcov_command += " --source-dirs=" + os.getcwd()
lcov_command += ":" + os.path.join(os.getcwd(),
variant.build_root,
'include')
gcov_env.Execute(lcov_command)
genhtml_command = "utils/lcov/genhtml -o " + lcov_report_dir
genhtml_command += " " + info_file
gcov_env.Execute(genhtml_command)
opt_env = env.Copy()
opt_env.Append(CFLAGS=opti_flags,
CXXFLAGS=opti_flags,
CPPDEFINES=['NDEBUG'])
# optimized static support
variant.static = True
variant.env = opt_env
variant.build_root = os.path.join(self.build_dir, 'opt-static')
builders = self.gen_mod_dep(variant)
for builder in builders:
opt_env.Alias('opt-static', builder)
opt_env = env.Copy()
opt_env.Append(CFLAGS = opti_flags,
CXXFLAGS = opti_flags,
CPPDEFINES = ['NDEBUG'])
# optimized shared support
variant.static = False
variant.env = opt_env
variant.build_root = os.path.join(self.build_dir, 'opt-shared')
builders = self.gen_mod_dep(variant)
for builder in builders:
opt_env.Alias('opt-shared', builder)
arc_env = env.Copy()
arc_env.Append(CFLAGS=opti_flags,
CXXFLAGS=opti_flags,
CPPDEFINES=['NDEBUG'])
arc_env.Append(CFLAGS=['-frandom-seed=0', '-fprofile-generate'],
CXXFLAGS=['-frandom-seed=0', '-fprofile-generate'],
LINKFLAGS=['-frandom-seed=0', '-fprofile-generate'])
# arc profiling
variant.static = False
variant.env = arc_env
variant.build_root = os.path.join(self.build_dir, 'opt-arc')
builders = self.gen_mod_dep(variant)
for builder in builders:
arc_env.Alias('opt-arc', builder)
arcrebuild_env = env.Copy()
arcrebuild_env.Append(CFLAGS=opti_flags,
CXXFLAGS=opti_flags,
CPPDEFINES=['NDEBUG'])
arcrebuild_env.Append(CFLAGS=['-frandom-seed=0', '-fprofile-use'],
CXXFLAGS=['-frandom-seed=0', '-fprofile-use'],
LINKFLAGS=['-frandom-seed=0', '-fprofile-use'])
# arc rebuild
variant.static = False
variant.env = arcrebuild_env
variant.gcxx_deps = True
variant.gcxx_root = os.path.join(self.build_dir, 'opt-arc')
variant.build_root = os.path.join(self.build_dir, 'opt-arc-rebuild')
builders = self.gen_mod_dep(variant)
for builder in builders:
arcrebuild_env.Alias('opt-arc-rebuild', builder)
variant.gcxx_deps = False
dbg_env = env.Copy()
dbg_env.Append(CFLAGS = debug_flags,
CXXFLAGS = debug_flags,
CPPDEFINES = ['NS3_DEBUG_ENABLE',
'NS3_ASSERT_ENABLE'])
# debug static support
variant.static = True
variant.env = dbg_env
variant.build_root = os.path.join(self.build_dir, 'dbg-static')
builders = self.gen_mod_dep(variant)
for builder in builders:
dbg_env.Alias('dbg-static', builder)
dbg_env = env.Copy()
dbg_env.Append(CFLAGS=debug_flags,
CXXFLAGS=debug_flags,
CPPDEFINES = ['NS3_DEBUG_ENABLE',
'NS3_ASSERT_ENABLE'])
# debug shared support
variant.static = False
variant.env = dbg_env
variant.build_root = os.path.join(self.build_dir, 'dbg-shared')
builders = self.gen_mod_dep(variant)
for builder in builders:
dbg_env.Alias('dbg-shared', builder)
env.Alias('dbg', 'dbg-shared')
env.Alias('opt', 'opt-shared')
env.Default('dbg')
env.Alias('all', ['dbg-shared', 'dbg-static', 'opt-shared', 'opt-static'])
# dist support
dist_env = env.Copy()
if dist_env['PLATFORM'] == 'posix' or dist_env['PLATFORM'] == 'darwin':
dist_list = []
for module in self.__modules:
for f in module.sources:
dist_list.append(os.path.join(module.dir, f))
for f in module.headers:
dist_list.append(os.path.join(module.dir, f))
for f in module.inst_headers:
dist_list.append(os.path.join(module.dir, f))
for f in module.extra_dist:
dist_list.append(os.path.join(module.dir, f))
for f in self.extra_dist:
dist_list.append(f)
dist_list.append (self.doxygen_config)
dist_list.append('SConstruct')
dist_list.append('build.py')
targets = []
basename = self.distname + '-' + self.version
for src in dist_list:
tgt = os.path.join(basename, src)
targets.append(dist_env.MyCopyBuilder(target=tgt, source=src))
tar = basename + '.tar.gz'
zip = basename + '.zip'
tmp_tar = os.path.join(self.build_dir, tar)
tmp_zip = os.path.join(self.build_dir, zip)
dist_tgt = [tar, zip]
dist_src = [tmp_tar, tmp_zip]
dist_env.Tar(tmp_tar, targets)
dist_env.Zip(tmp_zip, targets)
dist_builder = dist_env.MyCopyBuilder(target=dist_tgt, source=dist_src)
dist_env.Alias('dist', dist_builder)
dist_env.Append(RM_DIR=basename)
dist_env.AddPostAction(dist_builder, dist_env.Action(MyRmTree,
strfunction=MyRmTreePrint))
dist_builder = dist_env.MyCopyBuilder(target=dist_tgt, source=dist_src)
dist_env.Alias('fastdist', dist_tgt)
# distcheck
distcheck_list = []
for src in dist_list:
tgt = os.path.join(self.build_dir, basename, src)
distcheck_list.append(tgt)
untar = env.Command(distcheck_list, tar,
['cd ' + self.build_dir + ' && tar -zxf ../' + tar])
scons_dir = os.path.join(self.build_dir, basename)
distcheck_builder = env.Command('x', distcheck_list,
['cd ' + scons_dir + ' && scons'])
env.AlwaysBuild(distcheck_builder)
env.Alias('distcheck', distcheck_builder)
if self.doxygen_config != '':
doxy = env.Command('doc/html/*', self.doxygen_config,
['doxygen ' + self.doxygen_config])
env.AlwaysBuild(doxy)
env.Alias('doc', doxy)
+102
View File
@@ -0,0 +1,102 @@
The main ns-3 build system is SCons. Read the file build.txt
for SCons instructions.
Waf is an alternative build system, similar to SCons. ns-3 now is
able to build with Waf, in parallel to SCons.
(http://www.freehackers.org/~tnagy/waf.html)
Note: the Waf build scripts are experimental at this stage.
Gustavo Carneiro (gjcarneiro@gmail.com) is the maintainer.
=== Building with Waf ===
To build ns-3 with waf type the commands:
1. ./waf configure [options]
2. ./waf
[ Note: if ./waf does not exist, see the section "Note for developers" below ]
To see valid configure options, type ./waf --help. The most important
option is -d <debug level>. Valid debug levels (which are listed in
./waf --help) are: ultradebug, debug, release, and optimized.
The resulting binaries are placed in build/<debuglevel>/srcpath.
Other waf usages include:
1. ./waf check
Runs the unit tests
2. ./waf --doxygen
Run doxygen to generate documentation
3. ./waf --lcov-report
Run code coverage analysis (assuming the project was configured
with --enable-gcov)
4. ./waf --run "program [args]"
Run a ns3 program, given its target name, with the given
arguments. This takes care of automatically modifying the the
path for finding the ns3 dynamic libraries in the environment
before running the program. Note: the "program [args]" string is
parsed using POSIX shell rules.
5. ./waf --shell
Starts a nested system shell with modified environment to run ns3 programs.
=== Extending ns-3 ===
To add new modules:
1. Create the module directory under src (or src/devices, or whatever);
2. Add the source files to it;
3. Add a 'wscript' describing it;
4. Add the module subdirectory name to the all_modules list in src/wscript.
A module's wscript file is basically a regular Waf script. A ns-3
module is created as a cpp/shlib object, like this:
def build(bld):
obj = bld.create_obj('cpp', 'shlib')
## set module name; by convention it starts with ns3-
obj.name = 'ns3-mymodule'
obj.target = obj.name
## list dependencies to other modules
obj.uselib_local = ['ns3-core']
## list source files (private or public header files excluded)
obj.source = [
'mymodule.cc',
]
## list module public header files
headers = bld.create_obj('ns3header')
headers.source = [
'mymodule-header.h',
]
=== Note for developers ===
The ns-3 code repository does not contain the waf script. Instead,
developers should check it out from a subversion repository:
svn checkout http://waf.googlecode.com/svn/tags/ns3/ waf
[ note: 'tags/ns3' is a tag that represents the last svn version
tested to work correctly with ns3, although 'trunk' will likely work
as well ]
Then it can be installed system-wide with 'sudo ./waf-light install'.
When preparing a distribution, the resulting 'waf' script, which is
self contained (no external files needed), can be easily included in
the tarball so that users downloading ns-3 can easily build it without
having Waf installed (although Python >= 2.3 is still needed).
The command 'waf dist' can be used to create a distribution tarball.
It includes all files in the source directory, except some particular
extensions that are blacklisted, such as back files (ending in ~).
+159 -80
View File
@@ -1,107 +1,186 @@
The Waf build system is used to build ns-3. Waf is a Python-based
build system (http://www.freehackers.org/~tnagy/waf.html)
If you want to build ns3, you need to install scons (see
http://www.scons.org). scons takes care of building
the whole source tree using your system compiler. scons
0.91.1 and 0.91.96 have been tested and are known to
work on linux FC5, Mac os X and MinGW.
Note: We've added a wiki page with more complete build instructions
than the quick ones you find below:
http://www.nsnam.org/wiki/Installation
To start a build, you can just type 'scons' which
will generate a debug shared build by default, located
in the directory 'build-dir/dbg-shared/bin' and
'build-dir/dbg-shared/lib'.
=== Installing Waf ===
All builds are built with debugging symbols. Debugging
builds enable asserts while optimized builds disable them.
On platforms which support it, rpath is used which means that
the executable binaries generated link explicitely against
the right libraries. This saves you the pain of having to
setup environment variables to point to the right libraries.
The top-level ns-3 directory should contain a current waf script, so
there is no need to have WAF installed in the system. We are using
some extensions to WAF, which can be found in the 'waf-tools'
directory. The upstream location for these WAF extensions is:
(Note: An alternative build system (Waf) is being
evaluated in the development branch of ns-3-dev on our server
only (i.e., not in the release tarballs)-- see doc/build-waf.txt)
https://code.launchpad.net/~gjc/waf/cmd
1) Options
----------
- verbose: if you have installed scons 0.91.96 or higher,
the default build output is terse. To get a more verbose
output, you need to set the 'verbose' variable to 'y'.
Example: scons verbose=y
- cflags: flags for the C compiler.
Example: scons cflags="-O3 -ffast-math"
- cxxflags: flags for the C++ compiler.
Example: scons cxxflags="-O3 -ffast-math"
- ldflags: flags for the linker:
Example: scons ldflags="-L/foo -L/bar"
- cc: the C compiler to use:
Example: scons cc=gcc-4.0
- cxx: the C++ compiler to use:
Example: scons cxx=g++-4.0
- high-precision-as-double: set to 'y' to make sure that the
high-precision arithmetics performed by the Time class on
behalf of the user will use doubles. By default, the code
uses 128 integers.
Example: scons high-precision-as-double=y
- inheritenv: set to 'y' if you want to make your compiler
execute within the same environment (env vars) as your own
shell. This is typically used to make colorgcc work.
Example: scons inheritenv=y
=== Building with Waf ===
2) Targets
----------
To build ns-3 with waf type the commands from the top-level directory:
1. ./waf configure [options]
2. ./waf
- doc: build the doxygen documentation.
Example: scons doc
To see valid configure options, type ./waf --help. The most important
option is -d <debug level>. Valid debug levels (which are listed in
waf --help) are: "debug" or "optimized", with debug being default. It is
also possible to change the flags used for compilation with (e.g.):
CXXFLAGS="-O3" ./waf configure. By default, ns-3 is built as debug code,
with examples and tests disabled, and with python bindings enabled.
- dbg-shared: a debug build using shared libraries.
The files are built in 'build-dir/dbg-shared/'.
Example: scons dbg-shared
[ Note: Unlike some other build tools, to change the build target,
the option must be supplied during the configure stage rather than
the build stage (i.e., "./waf -d optimized" will not work; instead, do
"./waf -d optimized configure; ./waf" ]
- dbg-static: a debug build using static libraries
The files are built in 'build-dir/dbg-static/'.
Example: scons dbg-static
The resulting executables and libraries are placed in build/.
- opt-shared: an optimized build using shared libraries.
The files are built in 'build-dir/opt-shared/'.
Example: scons opt-shared
Other waf usages include:
- opt-static: an optimized build using static libraries.
The files are built in 'build-dir/opt-static/'.
Example: scons opt-static
1. ./waf configure --enable-examples --enable-tests
Turn on examples and tests.
- dbg: an alias for dbg-shared
Example: scons dbg
2. ./waf configure --disable-python
Disable python bindings.
- opt: an alias for opt-shared
Example: scons opt
3. ./waf --doxygen
Run doxygen to generate documentation
- all: alias for dbg-shared, dbg-static, opt-shared
and opt-static
Example: scons all
4. ./waf --run "program [args]"
Run a ns3 program, given its target name, with the given
arguments. This takes care of automatically modifying the the
path for finding the ns3 dynamic libraries in the environment
before running the program. Note: the "program [args]" string is
parsed using POSIX shell rules.
- gcov: code coverage analysis. Build a debugging version of
the code for code coverage analysis in 'build-dir/gcov'. Once
the code has been built, you can run various applications to
exercise the code paths. To generate an html report from
the gcov data, use the lcov-report target
4.1 ./waf --run programname --command-template "... %s ..."
- lcov-report: generate html report of gcov data. The output
is stored in 'build-dir/lcov-report/'.
Same as --run, but uses a command template with %s replaced by the
actual program (whose name is given by --run). This can be use to
run ns-3 programs with helper tools. For example, to run unit
tests with valgrind, use the command:
- dist: generate a release tarball and zipfile from the
source tree. The tarball and zipfile name are generated
according to the version number stored in the SConstruct
file.
Example in SConstruct:
ns3 = Ns3 ()
ns3.name = 'foo'
ns3.version = '0.0.10'
Example command: scons dist
Example output files:
foo-0.0.10.tar.gz
foo-0.0.10.zip
./waf --run run-tests --command-template "valgrind %s"
- distcheck: generate a release tarball and zipfile and
attempt to run the 'all' target for the release tarball.
Example: scons distcheck
5. ./waf --shell
Starts a nested system shell with modified environment to run ns3 programs.
3) How the build system works
-----------------------------
6. ./waf distclean
Cleans out the entire build/ directory
The current build system defines what are called "ns3 modules": each module
is a set of source files, normal header files and installable header
files. Each module also depends on a set of other modules. We build
modules automatically in the correct order. That is, we always start
from the module which does not depend on any other module (core) and
proceed with the other modules and make sure that when a module is
built, all the modules it depends upon have already been built.
7. ./waf dist
The command 'waf dist' can be used to create a distribution tarball.
It includes all files in the source directory, except some particular
extensions that are blacklisted, such as back files (ending in ~).
To build a module, we:
1) generate the .o files
2) link the .o files together
3) install the installable headers in the common directory
top_build_dir/include/ns3.
=== Extending ns-3 ===
This means that if you want to use a header from your own module, you
should just include it: #include "foo.h" but if you want to include a
header from another module, you need to include it with #include
"ns3/bar.h". This allows you to make sure that our "public" ns3 headers
do not conflict with existing system-level headers. For instance,
if you were to define a header called queue.h, you would include
ns3/queue.h rather than queue.h, when including from a separate module,
since many systems provide a queue.h system include file.
To add new modules:
1. Create the module directory under src;
2. Add the source files to it;
3. Add a 'wscript' describing it;
4) How to add files to a module ?
---------------------------------
A convenience program to auto-generate the template of a new module can
be found in src/create-module.py.
In the main SConstruct file, you can add source code
to the add_sources method. For example, to add a foo.cc
file to the core module, we coud do this:
core.add_sources ('foo.cc')
Of course, if this file implements public API, its
header should be installable:
core.add_inst_headers ('foo.h')
A module's wscript file is basically a regular Waf script. A ns-3
module is created as a cpp/shlib object, like this:
5) How to create a new module ?
-------------------------------
def build(bld):
module = bld.create_ns3_module('ns3-mymodule', ['core'])
module.source = [
'model/ns3-mymodule.cc',
'helper/ns3-mymodule-helper.cc',
]
headers = bld.new_task_gen(features=['ns3header'])
headers.module = 'ns3-mymodule'
headers.source = [
'model/ns3-mymodule.h',
'helper/ns3-mymodule-helper.h',
]
if bld.env.ENABLE_EXAMPLES:
bld.add_subdirs('examples')
# bld.ns3_python_bindings()
# create a new module. First arg is the name of
# the new module. Second arg is the directory in
# which all source files for this module reside.
my_module = build.Ns3Module ('my', 'src/my_dir')
# add it to build system
ns3.add (my_module)
# specify module dependencies. Here, depends
# on the 'ipv4' and 'core' modules
my_module.add_deps (['core', 'ipv4'])
# add source code to build located in
# src/my_dir
my_module.add_sources ([
'my_a.cc',
'my_b.cc',
'my_c.cc'
])
my_module.add_sources ([
'my_d.cc'
])
# add headers which are not public
my_module.add_headers ([
'my_a.h',
'my_c.h'
])
# add headers which are public
my_module.add_inst_headers ([
'my_b.h'
])
my_module.add_inst_headers ([
'my_d.h'
])
# if you need to link against an external library,
# you must add 'external' dependencies. Here, the
# pthread library
my_module.add_external_dep ('pthread')
# by default, a module is conceptually a library. If you
# want to generate an executable from a module you need to:
my_module.set_executable ()
+210 -2
View File
@@ -1,2 +1,210 @@
The project coding style document is maintained at:
http://www.nsnam.org/developers/contributing-code/coding-style/
The Ns-3 Coding Style
/*
* Note: This file is incomplete and will be converted to non-text (html,pdf)
* formats at a future date
*/
1) Code layout
-----------
The code layout follows the GNU coding standard layout for C and extends
it to C++. Do not use tabs for indentation. Indentation spacing is 2
spaces as outlined below:
void
Foo (void)
{
if (test)
{
// do stuff here
}
else
{
// do other stuff here
}
for (int i = 0; i < 100; i++)
{
// do loop
}
while (test)
{
// do while
}
do
{
// do stuff
} while ();
}
The following is not recommended:
if (test) statement
if (test)
statement
for (...) statement
Each statement should be put on a separate line to increase readability.
Short one-line comments can use the C++ comment style, that is, '//'
but longer comments should use C-style comments:
/*
*
*
*/
2) Naming Patterns
---------------
2.1) Name encoding
-------------
Function, Method, and Type names should follow the CamelCase convention:
words are joined without spaces and are capitalized. For example,
"my computer" is transformed into MyComputer. Do not use all capital
letters such as MAC or, PHY, but choose instead Mac or Phy. Do not use
all capital letters, even for acronyms such as EDCA: use Edca instead.
The goal of the CamelCase convention is to ensure that the words which
make up a name can be separated by the eye: the initial Caps fills
that role.
Variable names should follow a slight variation on the base CamelCase
convention: camelBack. For example, the variable "user name" would be
named "userName". This variation on the basic naming pattern is used to
allow a reader to distinguish a variable name from its type. For example,
"UserName userName;" would be used to declare a variable named userName
of type UserName.
Global variables should be prefixed with a "g_" and member variables
(including static member variables) should be prefixed with a "m_". The
goal of that prefix is to give a reader a sense of where a variable of
a given name is declared to allow the reader to locate the variable
declaration and infer the variable type from that declaration. For example
you could declare in your class header my-class.h:
class MyClass
{
void MyMethod (int aVar);
int m_aVar;
static int m_anotherVar;
};
and implement in your class file my-class.cc:
int MyClass::m_anotherVar = 10;
static int g_aStaticVar = 100;
int g_aGlobalVar = 1000;
void
MyClass::MyMethod (int aVar)
{
m_aVar = aVar;
}
2.2) Choosing names
Variable, function, method, and type names should be based on the
english language. Furthermore, always try to choose descriptive
names for them. Types are often english names such as: Packet,
Buffer, Mac, or Phy. Functions and Methods are often named
based on verbs and adjectives: GetX, DoDispose, ClearArray, etc.
A long descriptive name which requires a lot of typing is always
better than a short name which is hard to decipher. Do not use
abbreviations in names unless the abbreviation is really unambiguous
and obvious to everyone. Do not use short inapropriate names such
as foo, bar, or baz. The name of an item should always match its
purpose. As such, names such as tmp to identify a temporary
variable or such as 'i' to identify a loop index are ok.
3) File layout and code organization
---------------------------------
A class named MyClass should be declared in a header named my-class.h
and implemented in a source file named my-class.cc. The goal of this
naming pattern is to allow a reader to quickly navigate through
the ns-3 codebase to locate the source file relevant to a specific
type.
Each my-class.h header should start with the following comments: the
first line ensures that developers who use the emacs editor will be
able to indent your code correctly. The following lines ensure that
your code is licensed under the GPL, that the copyright holders
are properly identified (typically, you or your employer), and
that the actual author of the code is identified. The latter is
purely informational and we use it to try to track the most
appropriate person to review a patch or fix a bug.
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) YEAR COPYRIGHTHOLDER
*
* 3-paragran GPL blurb
*
* Author: MyName <myemail@foo.com>
*/
Below these C-style comments, always include the following which
defines a set of header guards (MY_CLASS_H) used to avoid multiple
header includes, which ensures that your code is included
in the "ns3" namespace and which provides a set of doxygen comments
for the public part of your class API. Detailed information
on the set of tags available for doxygen documentation is described
in the doxygen website: http://www.doxygen.org.
#ifndef MY_CLASS_H
#define MY_CLASS_H
namespace n3 {
/**
* \brief short one-line description of the purpose of your class
*
* A longer description of the purpose of your class after a blank
* empty line.
*/
class MyClass
{
public:
MyClass ();
/**
* \param firstParam a short description of the purpose of this parameter
* \returns a short description of what is returned from this function.
*
* A detailed description of the purpose of the method.
*/
int DoFoo (int firstParam);
private:
void MyPrivateMethod (void);
int m_myPrivateMemberVariable;
};
} // namespace ns3
#endif /* MY_CLASS_H */
The my-class.cc file is structured similarly:
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) YEAR COPYRIGHTHOLDER
*
* 3-paragran GPL blurb
*
* Author: MyName <myemail@foo.com>
*/
#include "my-class.h"
namespace ns3 {
MyClass::MyClass ()
{}
...
} // namespace ns3
+60 -4
View File
@@ -1,7 +1,63 @@
Contributing to the ns-3 project
--------------------------------
ns-3 is a free, open source software project that welcomes contributions
from users worldwide. Please see the following web page for how to
contribute:
http://www.nsnam.org/developers/contributing-code/
ns-3 is an open source project backed by an NSF CISE CRI grant.
Although the NSF PIs have specific aims to fulfill, we want others to
contribute, and we'd like to have a broad community of users and
developers, with the goal of a self-sustaining project downstream.
The project is currently in a bootstrapping phase, but we welcome
ambitious developers who might want to help shape the early design.
Despite the lack of a formal contribution process to the ns-3
project, there are a number of steps which we expect every
potential contributor to follow. These naturally stem from
the open-source roots of the project:
- first, you should subscribe to the ns-developers
mailing-list (see http://www.nsnam.org/mailing_lists.html)
- then, you should send an email there stating your interest
in working on a specific part of the models and trying
to explain how you would like to implement it, what
resources you have, etc.
- you should be prepared to work together with the other
potential contributors who want to work on the same models.
- you should be prepared to go through code reviews with the
ns-3 development team prior to integration. The goal of these
code reviews is to:
- ensure adherence to the coding style of the project
(see doc/codingstyle.html)
- ensure that the structure of your code has a certain
coherence with regard to the rest of the ns-3 codebase
- improve the quality of the code: we strongly believe in
the old saying: "many eyes make all bugs shallow".
- increase code reuse by trying to generalize certain
useful pieces of your code to make them available to
other models.
- you should be prepared to try to integrate as many tests
in the codebase as possible: we understand that writing
tests is not sexy and that not everyone is convinced that
they improve the code-writing poductivity which is why
we do not enforce strict rules about testing. However,
we expect model authors to answer basic questions about
how they plan to test and validate their models.
- you should be prepared to maintain your model once it is
integrated: while we consider every bug filed against the
simulator as being a bug we must deal with and while we
will try to fix as many bugs as humanly possible, we
also expect model authors to act as responsible maintainers
and be reactive to bug reports concerning their models.
- The project has decided upon GNU GPLv2 as the licensing structure.
All simulation software in the ns-3 repositories will be GNU GPLv2
or GNU GPLv2-compatible (with non-GPLv2 licensing reserved for
ports of pre-existing code under a different license, such as BSD).
You do not have to assign your copyright to the ns-3 project but
you must accept the terms of the GPLv2 and attest that your
contributions can be licensed under those terms. See the
following link:
http://www.fsf.org/licensing/licenses/info/GPLv2.html
+733 -1974
View File
File diff suppressed because it is too large Load Diff
-393
View File
@@ -1,393 +0,0 @@
#!/bin/bash
# Process doxygen log to generate sorted list of top offenders.
#
me=$(basename $0)
DIR="$(dirname $0)"
# Trick to get the absolute path, since doxygen prefixes errors that way
ROOT=$(cd "$DIR/.."; pwd)
# Known log files
STANDARDLOGFILE=doxygen.log
WARNINGSLOGFILE=doxygen.warnings.log
# Default choice: generate it
LOG="$DIR/$WARNINGSLOGFILE"
# Verbose log
VERBLOG="$DIR/doxygen.verbose.log"
# Options ------------------------------
#
function usage
{
cat <<-EOF
Usage: $me [-eithv] [-f <log-file> | -l | -s] [-m <module> | -F <regex>]
Run doxygen to generate all errors; report error counts
by module and file.
-i Skip build and print-introspected-doxygen.
-f Skip doxygen run; use existing <log-file>.
-s Skip doxygen run; use existing warnings log doc/$WARNINGSLOGFILE
-l Skip doxygen run; use the normal doxygen log doc/$STANDARDLOGFILE
-e Filter out warnings from */examples/*
-t Filter out warnings from */test/*
-m Only include files matching src/<module>
-F Only include files matching the <regex>
-v Show the doxygen run output
-h Print this usage message
The default behavior is to modify doxygen.conf temporarily to
report all undocumented elements, and to reduce the run time.
The output of this special run is kept in doc/$WARNINGSLOGFILE.
To further reduce the run time, the -i option also skips
print-introspected-doxygen, so waf doesn\'t have to compile
any modified files at all.
The -f, -l, and -s options skip the doxygen run altogether.
The first two use a specified or the standard log file;
the -s option uses the warnings log from a prior run.
Only the first of -f <log-file>, -s, or -l will have effect.
The -e and -t options exclude examples and test directories
from the counts. The -m option only includes a specific module.
The -F option only includes files (or warnings) matching the <regex>.
The -m and -F options append the relevant warnings after the
numerical report. These can be used in any combination.
EOF
exit 1
}
# Messaging ----------------------------
#
# Arg -v Verbosity level
verbosity=0
function verbose
{
if [ "$1" == "-n" ]; then
echo -n "$2"
elif [ $verbosity -eq 1 ]; then
echo "$1 $2"
else
echo "$2"
fi
}
# Use file handle 6 for verbose output
rm -f $VERBLOG
exec 6>$VERBLOG
function status_report
{
local status="$1"
local long_msg="$2"
if [ $status -eq 0 ]; then
verbose "$long_msg " "done."
rm -f $VERBLOG
else
verbose "$long_msg " "FAILED. Details:"
cat $VERBLOG
rm -f $VERBLOG
exit 1
fi
}
# Argument processing ------------------
#
# -f argument
use_filearg=0
logfile_arg=
# -l
use_standard=0
# skip doxygen run; using existing log file
skip_doxy=0
# skip print-introspected-doxygen, avoiding a build
skip_intro=0
# Filtering flags
filter_examples=0
filter_test=0
filter_module=""
filter_regex=""
echo
echo "$me:"
while getopts :eitm:F:lF:svh option ; do
case $option in
(e) filter_examples=1 ;;
(i) skip_intro=1 ;;
(t) filter_test=1 ;;
(m) filter_module="$OPTARG" ;;
(F) filter_regex="$OPTARG" ;;
(l) use_standard=1 ;;
(f) use_filearg=1
logfile_arg="$OPTARG"
;;
(s) use_filearg=1
logfile_arg="$DIR/$WARNINGSLOGFILE"
;;
(v) verbosity=1
exec 6>&1
;;
(h) usage ;;
(:) echo "$me: Missing argument to -$OPTARG" ; usage ;;
(\?) echo "$me: Invalid option: -$OPTARG" ; usage ;;
esac
done
function checklogfile
{
if [ -e "$1" ] ; then
skip_doxy=1
LOG="$1"
else
echo "$me: log file $1 does not exist."
exit 1
fi
}
# Which log file
if [[ $use_filearg -eq 1 && "${logfile_arg:-}" != "" ]] ; then
checklogfile "$logfile_arg"
elif [ $use_standard -eq 1 ]; then
checklogfile "$DIR/$STANDARDLOGFILE"
fi
# Run doxygen -------------------------
#
if [ $skip_doxy -eq 1 ]; then
echo
echo "Skipping doxygen run, using existing log file $LOG"
else
if [ $skip_intro -eq 1 ]; then
verbose "" "Skipping ./waf build and print-introspected-doxygen."
else
# Run introspection, which may require a build
verbose -n "Building and running print-introspected-doxygen..."
(cd "$ROOT" && ./waf --run print-introspected-doxygen >doc/introspected-doxygen.h >&6 2>&6 )
status_report $? "./waf build"
fi
# Modify doxygen.conf to generate all the warnings
# (We also suppress dot graphs, so shorten the run time.)
conf=$DIR/doxygen.conf
sed -i.bak -E '/^EXTRACT_ALL |^HAVE_DOT |^WARNINGS /s/YES/no/' $conf
verbose -n "Rebuilding doxygen (v$(doxygen --version)) docs with full errors..."
(cd "$ROOT" && ./waf --doxygen-no-build >&6 2>&6 )
status=$?
rm -f $conf
mv -f $conf.bak $conf
status_report $status "Doxygen run"
cp -f "$DIR/$STANDARDLOGFILE" "$DIR/$WARNINGSLOGFILE"
fi
# Log filters --------------------------
#
# Filter regular expression for -m and -F
filter_inRE=""
if [ "$filter_module" != "" ] ; then
filter_inRE="${filter_inRE:-}${filter_inRE:+\\|}src/$filter_module"
fi
if [ "$filter_regex" != "" ] ; then
filter_inRE="${filter_inRE:-}${filter_inRE:+\\|}$filter_regex"
fi
# Filter regular expression for -e and -t
filter_outRE=""
if [ $filter_examples -eq 1 ]; then
filter_outRE="${filter_outRE:-}${filter_outRE:+\\|}/examples/"
fi
if [ $filter_test -eq 1 ]; then
filter_outRE="${filter_outRE:-}${filter_outRE:+\\|}/test/"
fi
if [ "${filter_inRE:-}" != "" ] ; then
echo "Filtering in \"$filter_inRE\""
fi
if [ "${filter_outRE:-}" != "" ] ; then
echo "Filtering out \"$filter_outRE\""
fi
echo
# Filter log file
function filter_log
{
local flog;
flog=$( cat "$LOG" | grep "^$ROOT" )
if [ "${filter_inRE:-}" != "" ] ; then
flog=$( echo "$flog" | grep "$filter_inRE" )
fi
if [ "${filter_outRE:-}" != "" ] ; then
flog=$( echo "$flog" | grep -v "$filter_outRE" )
fi
flog=$( \
echo "$flog" | \
sort -t ':' -k1,1 -k2,2n | \
uniq \
)
echo "$flog"
}
# Analyze the log ----------------------
#
# List of module directories (e.g, "src/core/model")
undocmods=$( \
filter_log | \
cut -d ':' -f 1 | \
sed "s|$ROOT/||g" | \
cut -d '/' -f 1-3 | \
sort | \
uniq -c | \
sort -nr \
)
# Number of directories
modcount=$( \
echo "$undocmods" | \
wc -l | \
sed 's/^[ \t]*//;s/[ \t]*$//' \
)
# For a function with multiple undocumented parameters,
# Doxygen prints the additional parameters on separate lines,
# so they don't show up in the totals above.
# Rather than work too hard to get the exact number for each file,
# we just list the total here.
addlparam=$( \
grep "^ parameter '" "$LOG" | \
wc -l | \
sed 's/^[ \t]*//;s/[ \t]*$//' \
)
# Total number of warnings
warncount=$( \
echo "$undocmods" | \
awk '{total += $1}; END {print total}' \
)
warncount=$((warncount + addlparam))
# List of files appearing in the log
undocfiles=$( \
filter_log | \
cut -d ':' -f 1 | \
sed "s|$ROOT||g" | \
cut -d '/' -f 2- | \
sort | \
uniq -c | \
sort -k 2 \
)
# Sorted by number, decreasing
undocsort=$(echo "$undocfiles" | sort -k1nr,2 )
# Total number of files
filecount=$( \
echo "$undocfiles" | \
wc -l | \
sed 's/^[ \t]*//;s/[ \t]*$//' \
)
# Filtered in warnings
filterin=
if [ "${filter_inRE:-}" != "" ] ; then
filterin=$( \
filter_log | \
sed "s|$ROOT/||g" \
)
fi
# Summarize the log --------------------
#
echo
echo "Report of Doxygen warnings"
echo "----------------------------------------"
echo
echo "(All counts are lower bounds.)"
echo
echo "Warnings by module/directory:"
echo
echo "Count Directory"
echo "----- ----------------------------------"
echo "$undocmods"
echo " $addlparam additional undocumented parameters."
echo "----------------------------------------"
printf "%6d total warnings\n" $warncount
printf "%6d directories with warnings\n" $modcount
echo
echo
echo "Warnings by file (alphabetical)"
echo
echo "Count File"
echo "----- ----------------------------------"
echo "$undocfiles"
echo "----------------------------------------"
printf "%6d files with warnings\n" $filecount
echo
echo
echo "Warnings by file (numerical)"
echo
echo "Count File"
echo "----- ----------------------------------"
echo "$undocsort"
echo "----------------------------------------"
printf "%6d files with warnings\n" $filecount
echo
echo
echo "Doxygen Warnings Summary"
echo "----------------------------------------"
printf "%6d directories\n" $modcount
printf "%6d files\n" $filecount
printf "%6d warnings\n" $warncount
if [ "$filterin" != "" ] ; then
echo
echo
echo "Filtered Warnings"
echo "========================================"
echo "$filterin"
fi
-97
View File
@@ -1,97 +0,0 @@
/**
* \mainpage ns-3 Documentation
*
* \section intro-sec Introduction
* <a href="http://www.nsnam.org/">ns-3</a> documentation is maintained using
* <a href="http://www.doxygen.org">Doxygen</a>.
* Doxygen is typically used for
* API documentation, and organizes such documentation across different
* modules. This project uses Doxygen for building the definitive
* maintained API documentation. Additional ns-3 project documentation
* can be found at the
* <a href="http://www.nsnam.org/documentation/latest">project web site</a>.
*
* \section install-sec Building the Documentation
*
* ns-3 requires Doxygen version 1.8.3.1 or greater.
*
* Type "./waf --doxygen" or "./waf --doxygen-no-build" to build the
* documentation. The doc/ directory contains
* configuration for Doxygen (doxygen.conf) and main.h. The Doxygen
* build process puts html files into the doc/html/ directory, and latex
* filex into the doc/latex/ directory.
*
* \section module-sec Module overview
*
* The ns-3 library is split across many modules organized under the
* <b><a href="modules.html">Modules</a></b> tab.
* - aodv
* - applications
* - bridge
* - click
* - config-store
* - core
* - csma
* - csma-layout
* - dsdv
* - emu
* - energy
* - flow-monitor
* - internet
* - lte
* - mesh
* - mobility
* - mpi
* - netanim
* - network
* - nix-vector-routing
* - ns3tcp
* - ns3wifi
* - olsr
* - openflow
* - point-to-point
* - point-to-point-layout
* - propagation
* - spectrum
* - stats
* - tap-bridge
* - test
* - topology-read
* - uan
* - virtual-net-device
* - visualizer
* - wifi
* - wimax
*
*/
/**
* \namespace ns3
* \brief Every class exported by the ns3 library is enclosed in the
* ns3 namespace.
*/
// Macros defined by the build system.
//
// These have to be visible for doxygen to document them,
// so we put them here in a file only seen by doxygen, not the compiler.
/**
* \ingroup assert
*
* \def NS3_ASSERT_ENABLE
*
* Enable asserts at compile time.
*
* This is normally set by `./waf configure --build-profile=debug`.
*/
#define NS3_ASSERT_ENABLE
/**
* \ingroup logging
*
* \def NS3_LOG_ENABLE
*
* Enable logging at compile time.
*
* This is normally set by `./waf configure --build-profile=debug`.
*/
#define NS3_LOG_ENABLE
+68
View File
@@ -0,0 +1,68 @@
/**
* \mainpage An Introduction to ns-3
*
* The ns-3 library is split across multiple modules:
* - core: located in src/core and contains a number of facilities which
* do not depend on any other module. Some of these facilities are
* OS-dependent.
* - simulator: located in src/simulator and contains event scheduling
* facilities.
* - common: located in src/common and contains facilities specific
* to network simulations but shared by pretty much every model
* of a network component.
* - node: located in src/node. Defines the abstract interfaces which
* must be implemented by every node and more specifically, by ipv4 nodes.
* - devices: located in src/devices. Contains a set of MAC-level models
*
* The "core" module contains:
* - a Functor class: ns3::Callback
* - an os-independent interface to get access to the elapsed wall clock time: ns3::SystemWallClockMs
* - a class to register regression tests with the test manager: ns3::Test and ns3::TestManager
* - debugging facilities: \ref debugging, \ref assert, \ref error
* - \ref randomvariable
* - \ref config
* - a base class for objects which need to support reference counting
* and QueryInterface: ns3::Object and ns3::InterfaceId
* - a ns3::ComponentManager which can be used to manage the creation
* of any object which derives from ns3::Object through an ns3::ClassId
* - a smart-pointer class ns3::Ptr designed to work together with ns3::Object
*
* The "simulator" module contains:
* - a time management class to hold a time and convert between various time units: ns3::Time
* - a scheduler base class used to implement new simulation event schedulers:
* ns3::Scheduler and ns3::SchedulerFactory
* - a simulator class used to create, schedule and cancel events: ns3::Simulator
*
* The "common" module contains:
* - a packet class to create and manipulate simulation packets: ns3::Packet, ns3::Header,
* and ns3::Trailer. This packet class also supports per-packet ns3::Tag which are
* globs of data which can be attached to any packet.
* - a set of low-level trace facilities: \ref lowleveltracing
*
* The "node" module contains:
* - a ns3::Node base class which should be subclassed by any new type of
* network Node.
* - models which abstract the MAC-layer from the IP layer protocols:
* ns3::NetDevice and ns3::Channel.
* - models which abstract the application-layer API: ns3::Application,
* ns3::Socket, ns3::SocketFactory, and, ns3::Udp
*
* The "internet-node" module contains a set of classes which implement the
* APIs defined in the "node" module:
* - an Ipv4/Udp stack with socket support
* - an ARP module
* - an InternetNode class which is a Node subclass.
*
* The "devices" module contains:
* - a PointToPoint MAC device: ns3::PointToPointNetDevice, ns3::PointToPointChannel,
* and ns3::PointToPointTopology.
*/
/**
* \namespace ns3
* \brief Every class exported by the ns3 library is enclosed in the
* ns3 namespace.
*/
/**
* \defgroup constants Constants
* \brief Constants you can change
*/
-254
View File
@@ -1,254 +0,0 @@
EPSTOPDF = epstopdf
DIA = dia
CONVERT = convert
SRC = ../../src
# Temporary source directory, for build
SOURCETEMP = source-temp
FIGURES = $(SOURCETEMP)/figures
#VPATH = $(FIGURES)
# list all manual .rst files that need to be copied to $SOURCETEMP
SOURCES = \
source/conf.py \
source/_static \
source/index.rst \
source/replace.txt \
source/attributes.rst \
source/callbacks.rst \
source/documentation.rst \
source/enable-modules.rst \
source/enable-tests.rst \
source/events.rst \
source/gnuplot.rst \
source/hash-functions.rst \
source/helpers.rst \
source/how-to-write-tests.rst \
source/logging.rst \
source/new-models.rst \
source/new-modules.rst \
source/object-model.rst \
source/object-names.rst \
source/organization.rst \
source/python.rst \
source/random-variables.rst \
source/realtime.rst \
source/support.rst \
source/test-background.rst \
source/test-framework.rst \
source/test-overview.rst \
source/tests.rst \
source/tracing.rst \
source/troubleshoot.rst \
${SRC}/stats/doc/data-collection.rst \
${SRC}/stats/doc/data-collection-overview.rst \
${SRC}/stats/doc/statistics.rst \
${SRC}/stats/doc/data-collection-helpers.rst \
${SRC}/stats/doc/probe.rst \
${SRC}/stats/doc/collector.rst \
${SRC}/stats/doc/aggregator.rst \
${SRC}/stats/doc/adaptor.rst \
${SRC}/stats/doc/scope-and-limitations.rst \
# list all manual figure files that need to be copied to
# $SOURCETEMP/figures. For each figure to be included in all
# documentation formats (html, latex...) the following formats are supported:
# 1) a single .dia file (preferred option, because it can be edited)
# 2) a single .eps file
# 3) both a .pdf and .png file
SOURCEFIGS = \
figures/software-organization.dia \
figures/plot-2d.png \
figures/plot-2d-with-error-bars.png \
figures/plot-3d.png \
${SRC}/stats/doc/Stat-framework-arch.png \
${SRC}/stats/doc/Wifi-default.png \
${SRC}/stats/doc/dcf-overview.dia \
${SRC}/stats/doc/dcf-overview-with-aggregation.dia \
${SRC}/stats/doc/gnuplot-example.png \
${SRC}/stats/doc/file-example.png \
${SRC}/stats/doc/seventh-packet-byte-count.png \
${SRC}/stats/doc/gnuplot-helper-example.png \
${SRC}/stats/doc/gnuplot-aggregator.png \
# specify figures from which .png and .pdf figures need to be
# generated (all dia and eps figures)
IMAGES_EPS = \
$(FIGURES)/software-organization.eps \
$(FIGURES)/dcf-overview.eps \
$(FIGURES)/dcf-overview-with-aggregation.eps \
# rescale pdf figures as necessary
$(FIGURES)/software-organization.pdf_width = 5in
IMAGES_PNG = $(IMAGES_EPS:.eps=.png)
IMAGES_PDF = ${IMAGES_EPS:.eps=.pdf}
IMAGES = $(IMAGES_EPS) $(IMAGES_PNG) $(IMAGES_PDF)
RESCALE = ../../utils/rescale-pdf.sh
%.eps : %.dia
@echo dia $(notdir $<)
@$(DIA) -t eps $< -e $@ >/dev/null
%.png : %.dia
@echo dia $(notdir $<)
@$(DIA) -t png $< -e $@ >/dev/null
%.png : %.eps
@echo convert $(notdir $<)
@$(CONVERT) $< $@ >/dev/null
%.pdf : %.eps
@echo epstopdf $(notdir $<)
@$(EPSTOPDF) $< -o=$@ >/dev/null
@if test x$($@_width) != x; then $(RESCALE) $($@_width) $@ ; fi
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SOURCETEMP)
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
.NOTPARALLEL:
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
copy-sources: $(SOURCES)
@rm -rf $(SOURCETEMP)
@mkdir -p $(SOURCETEMP)
@mkdir -p $(FIGURES)
@cp -r $(SOURCES) $(SOURCETEMP)
@cp -r $(SOURCEFIGS) $(FIGURES)
clean:
-rm -rf $(BUILDDIR)
-rm -rf $(SOURCETEMP)
frag: pickle
@if test ! -d $(BUILDDIR)/frag; then mkdir $(BUILDDIR)/frag; fi
pushd $(BUILDDIR)/frag && ../../pickle-to-xml.py ../pickle/index.fpickle > navigation.xml && popd
cp -r $(BUILDDIR)/pickle/_images $(BUILDDIR)/frag
html: copy-sources $(IMAGES)
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml: copy-sources $(IMAGES)
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml: copy-sources $(IMAGES)
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle: copy-sources $(IMAGES)
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json: copy-sources $(IMAGES)
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp: copy-sources $(IMAGES)
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp: copy-sources $(IMAGES)
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ns-3.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ns-3.qhc"
devhelp: copy-sources $(IMAGES)
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/ns-3"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ns-3"
@echo "# devhelp"
epub: copy-sources $(IMAGES)
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex: copy-sources $(IMAGES)
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf: copy-sources $(IMAGES)
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
make -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text: copy-sources $(IMAGES)
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man: copy-sources $(IMAGES)
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
changes: copy-sources $(IMAGES)
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck: copy-sources $(IMAGES)
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest: copy-sources $(IMAGES)
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
test: $(IMAGES)
-18
View File
@@ -1,18 +0,0 @@
Please write image files in a vector graphics format, when possible, and
generate the .png and .pdf versions on the fly (see ../Makefile).
The currently supported tool is dia. xfig could be added similarly
if someone wants to add it. The main requirement for adding another format
is that the tool to edit it is freely available and that a cron script can
autogenerate the pdf and png from the figure source. Tgif (.obj) files
were once used but the file conversions require a valid X display to
be running, and are therefore to be avoided since our code server
does not run such a server. Tgif pdf conversions were also cumbersome.
Store the .dia versions in mercurial, but not the .png or .pdfs.
If the figure is not available in a vector graphics format, store both
a .png and a .pdf version in this directory.
If you add a source (.dia) file here, remember to add it to
the list of figure sources in the Makefile in the directory above
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.
View File
File diff suppressed because it is too large Load Diff
-610
View File
@@ -1,610 +0,0 @@
.. include:: replace.txt
.. highlight:: cpp
Callbacks
---------
Some new users to |ns3| are unfamiliar with an extensively used programming
idiom used throughout the code: the *ns-3 callback*. This chapter provides some
motivation on the callback, guidance on how to use it, and details on its
implementation.
Callbacks Motivation
********************
Consider that you have two simulation models A and B, and you wish to have them
pass information between them during the simulation. One way that you can do
that is that you can make A and B each explicitly knowledgeable about the other,
so that they can invoke methods on each other::
class A {
public:
void ReceiveInput ( // parameters );
...
}
(in another source file:)
class B {
public:
void DoSomething (void);
...
private:
A* a_instance; // pointer to an A
}
void
B::DoSomething()
{
// Tell a_instance that something happened
a_instance->ReceiveInput ( // parameters);
...
}
This certainly works, but it has the drawback that it introduces a dependency on
A and B to know about the other at compile time (this makes it harder to have
independent compilation units in the simulator) and is not generalized; if in a
later usage scenario, B needs to talk to a completely different C object, the
source code for B needs to be changed to add a ``c_instance`` and so forth. It
is easy to see that this is a brute force mechanism of communication that can
lead to programming cruft in the models.
This is not to say that objects should not know about one another if there is a
hard dependency between them, but that often the model can be made more flexible
if its interactions are less constrained at compile time.
This is not an abstract problem for network simulation research, but rather it
has been a source of problems in previous simulators, when researchers want to
extend or modify the system to do different things (as they are apt to do in
research). Consider, for example, a user who wants to add an IPsec security
protocol sublayer between TCP and IP::
------------ -----------
| TCP | | TCP |
------------ -----------
| becomes -> |
----------- -----------
| IP | | IPsec |
----------- -----------
|
-----------
| IP |
-----------
If the simulator has made assumptions, and hard coded into the code, that IP
always talks to a transport protocol above, the user may be forced to hack the
system to get the desired interconnections. This is clearly not an optimal way
to design a generic simulator.
Callbacks Background
********************
.. note:: Readers familiar with programming callbacks may skip this tutorial
section.
The basic mechanism that allows one to address the problem above is known as a
*callback*. The ultimate goal is to allow one piece of code to call a function
(or method in C++) without any specific inter-module dependency.
This ultimately means you need some kind of indirection -- you treat the address
of the called function as a variable. This variable is called a
pointer-to-function variable. The relationship between function and
pointer-to-function pointer is really no different that that of object and
pointer-to-object.
In C the canonical example of a pointer-to-function is a
pointer-to-function-returning-integer (PFI). For a PFI taking one int parameter,
this could be declared like,::
int (*pfi)(int arg) = 0;
What you get from this is a variable named simply ``pfi`` that is initialized to
the value 0. If you want to initialize this pointer to something meaningful, you
have to have a function with a matching signature. In this case::
int MyFunction (int arg) {}
If you have this target, you can initialize the variable to point to your
function like::
pfi = MyFunction;
You can then call MyFunction indirectly using the more suggestive form of the
call::
int result = (*pfi) (1234);
This is suggestive since it looks like you are dereferencing the function
pointer just like you would dereference any pointer. Typically, however, people
take advantage of the fact that the compiler knows what is going on and will
just use a shorter form::
int result = pfi (1234);
Notice that the function pointer obeys value semantics, so you can pass it
around like any other value. Typically, when you use an asynchronous interface
you will pass some entity like this to a function which will perform an action
and *call back* to let you know it completed. It calls back by following the
indirection and executing the provided function.
In C++ you have the added complexity of objects. The analogy with the PFI above
means you have a pointer to a member function returning an int (PMI) instead of
the pointer to function returning an int (PFI).
The declaration of the variable providing the indirection looks only slightly
different::
int (MyClass::*pmi) (int arg) = 0;
This declares a variable named ``pmi`` just as the previous example declared a
variable named ``pfi``. Since the will be to call a method of an instance of a
particular class, one must declare that method in a class::
class MyClass {
public:
int MyMethod (int arg);
};
Given this class declaration, one would then initialize that variable like
this::
pmi = &MyClass::MyMethod;
This assigns the address of the code implementing the method to the variable,
completing the indirection. In order to call a method, the code needs a ``this``
pointer. This, in turn, means there must be an object of MyClass to refer to. A
simplistic example of this is just calling a method indirectly (think virtual
function)::
int (MyClass::*pmi) (int arg) = 0; // Declare a PMI
pmi = &MyClass::MyMethod; // Point at the implementation code
MyClass myClass; // Need an instance of the class
(myClass.*pmi) (1234); // Call the method with an object ptr
Just like in the C example, you can use this in an asynchronous call to another
module which will *call back* using a method and an object pointer. The
straightforward extension one might consider is to pass a pointer to the object
and the PMI variable. The module would just do::
(*objectPtr.*pmi) (1234);
to execute the callback on the desired object.
One might ask at this time, *what's the point*? The called module will have to
understand the concrete type of the calling object in order to properly make the
callback. Why not just accept this, pass the correctly typed object pointer and
do ``object->Method(1234)`` in the code instead of the callback? This is
precisely the problem described above. What is needed is a way to decouple the
calling function from the called class completely. This requirement led to the
development of the *Functor*.
A functor is the outgrowth of something invented in the 1960s called a closure.
It is basically just a packaged-up function call, possibly with some state.
A functor has two parts, a specific part and a generic part, related through
inheritance. The calling code (the code that executes the callback) will execute
a generic overloaded ``operator ()`` of a generic functor to cause the callback
to be called. The called code (the code that wants to be called back) will have
to provide a specialized implementation of the ``operator ()`` that performs the
class-specific work that caused the close-coupling problem above.
With the specific functor and its overloaded ``operator ()`` created, the called
code then gives the specialized code to the module that will execute the
callback (the calling code).
The calling code will take a generic functor as a parameter, so an implicit cast
is done in the function call to convert the specific functor to a generic
functor. This means that the calling module just needs to understand the
generic functor type. It is decoupled from the calling code completely.
The information one needs to make a specific functor is the object pointer and
the pointer-to-method address.
The essence of what needs to happen is that the system declares a generic part
of the functor::
template <typename T>
class Functor
{
public:
virtual int operator() (T arg) = 0;
};
The caller defines a specific part of the functor that really is just there to
implement the specific ``operator()`` method::
template <typename T, typename ARG>
class SpecificFunctor : public Functor<ARG>
{
public:
SpecificFunctor(T* p, int (T::*_pmi)(ARG arg))
{
m_p = p;
m_pmi = _pmi;
}
virtual int operator() (ARG arg)
{
(*m_p.*m_pmi)(arg);
}
private:
int (T::*m_pmi)(ARG arg);
T* m_p;
};
Here is an example of the usage::
class A
{
public:
A (int a0) : a (a0) {}
int Hello (int b0)
{
std::cout << "Hello from A, a = " << a << " b0 = " << b0 << std::endl;
}
int a;
};
int main()
{
A a(10);
SpecificFunctor<A, int> sf(&a, &A::Hello);
sf(5);
}
.. note:: The previous code is not real ns-3 code. It is simplistic example
code used only to illustrate the concepts involved and to help you understand
the system more. Do not expect to find this code anywhere in the ns-3 tree.
Notice that there are two variables defined in the class above. The m_p
variable is the object pointer and m_pmi is the variable containing the
address of the function to execute.
Notice that when ``operator()`` is called, it in turn calls the method provided
with the object pointer using the C++ PMI syntax.
To use this, one could then declare some model code that takes a generic functor
as a parameter::
void LibraryFunction (Functor functor);
The code that will talk to the model would build a specific functor and pass it to ``LibraryFunction``::
MyClass myClass;
SpecificFunctor<MyClass, int> functor (&myclass, MyClass::MyMethod);
When ``LibraryFunction`` is done, it executes the callback using the
``operator()`` on the generic functor it was passed, and in this particular
case, provides the integer argument::
void
LibraryFunction (Functor functor)
{
// Execute the library function
functor(1234);
}
Notice that ``LibraryFunction`` is completely decoupled from the specific
type of the client. The connection is made through the Functor polymorphism.
The Callback API in |ns3| implements object-oriented callbacks using
the functor mechanism. This callback API, being based on C++ templates, is
type-safe; that is, it performs static type checks to enforce proper signature
compatibility between callers and callees. It is therefore more type-safe to
use than traditional function pointers, but the syntax may look imposing at
first. This section is designed to walk you through the Callback system so
that you can be comfortable using it in |ns3|.
Using the Callback API
**********************
The Callback API is fairly minimal, providing only two services:
1. callback type declaration: a way to declare a type of callback
with a given signature, and,
2. callback instantiation: a way to instantiate a
template-generated forwarding callback which can forward any calls
to another C++ class member method or C++ function.
This is best observed via walking through an example, based on
``samples/main-callback.cc``.
Using the Callback API with static functions
++++++++++++++++++++++++++++++++++++++++++++
Consider a function::
static double
CbOne (double a, double b)
{
std::cout << "invoke cbOne a=" << a << ", b=" << b << std::endl;
return a;
}
Consider also the following main program snippet::
int main (int argc, char *argv[])
{
// return type: double
// first arg type: double
// second arg type: double
Callback<double, double, double> one;
}
This is an example of a C-style callback -- one which does not include or need
a ``this`` pointer. The function template ``Callback`` is essentially the
declaration of the variable containing the pointer-to-function. In the example
above, we explicitly showed a pointer to a function that returned an integer and
took a single integer as a parameter, The ``Callback`` template function is
a generic version of that -- it is used to declare the type of a callback.
.. note:: Readers unfamiliar with C++ templates may consult `<http://www.cplusplus.com/doc/tutorial/templates/>`_.
The ``Callback`` template requires one mandatory argument (the return type
of the function to be assigned to this callback) and up to five optional
arguments, which each specify the type of the arguments (if your particular
callback function has more than five arguments, then this can be handled
by extending the callback implementation).
So in the above example, we have a declared a callback named "one" that will
eventually hold a function pointer. The signature of the function that it will
hold must return double and must support two double arguments. If one tries
to pass a function whose signature does not match the declared callback,
a compilation error will occur. Also, if one tries to assign to a callback
an incompatible one, compilation will succeed but a run-time
NS_FATAL_ERROR will be raised. The sample program
``src/core/examples/main-callback.cc`` demonstrates both of these error cases
at the end of the ``main()`` program.
Now, we need to tie together this callback instance and the actual target function
(CbOne). Notice above that CbOne has the same function signature types as the
callback-- this is important. We can pass in any such properly-typed function
to this callback. Let's look at this more closely::
static double CbOne (double a, double b) {}
^ ^ ^
| | |
| | |
Callback<double, double, double> one;
You can only bind a function to a callback if they have the matching signature.
The first template argument is the return type, and the additional template
arguments are the types of the arguments of the function signature.
Now, let's bind our callback "one" to the function that matches its signature::
// build callback instance which points to cbOne function
one = MakeCallback (&CbOne);
This call to ``MakeCallback`` is, in essence, creating one of the specialized
functors mentioned above. The variable declared using the ``Callback``
template function is going to be playing the part of the generic functor. The
assignment ``one = MakeCallback (&CbOne)`` is the cast that converts the
specialized functor known to the callee to a generic functor known to the caller.
Then, later in the program, if the callback is needed, it can be used as follows::
NS_ASSERT (!one.IsNull ());
// invoke cbOne function through callback instance
double retOne;
retOne = one (10.0, 20.0);
The check for ``IsNull()`` ensures that the callback is not null -- that there
is a function to call behind this callback. Then, ``one()`` executes the
generic ``operator()`` which is really overloaded with a specific implementation
of ``operator()`` and returns the same result as if ``CbOne()`` had been
called directly.
Using the Callback API with member functions
++++++++++++++++++++++++++++++++++++++++++++
Generally, you will not be calling static functions but instead public member
functions of an object. In this case, an extra argument is needed to the
MakeCallback function, to tell the system on which object the function should be
invoked. Consider this example, also from main-callback.cc::
class MyCb {
public:
int CbTwo (double a) {
std::cout << "invoke cbTwo a=" << a << std::endl;
return -5;
}
};
int main ()
{
...
// return type: int
// first arg type: double
Callback<int, double> two;
MyCb cb;
// build callback instance which points to MyCb::cbTwo
two = MakeCallback (&MyCb::CbTwo, &cb);
...
}
Here, we pass an additional object pointer to the ``MakeCallback<>`` function.
Recall from the background section above that ``Operator()`` will use the pointer to
member syntax when it executes on an object::
virtual int operator() (ARG arg)
{
(*m_p.*m_pmi)(arg);
}
And so we needed to provide the two variables (``m_p`` and ``m_pmi``) when
we made the specific functor. The line::
two = MakeCallback (&MyCb::CbTwo, &cb);
does precisely that. In this case, when ``two ()`` is invoked::
int result = two (1.0);
will result in a call tothe ``CbTwo`` member function (method) on the object
pointed to by ``&cb``.
Building Null Callbacks
+++++++++++++++++++++++
It is possible for callbacks to be null; hence it may be wise to
check before using them. There is a special construct for a null
callback, which is preferable to simply passing "0" as an argument;
it is the ``MakeNullCallback<>`` construct::
two = MakeNullCallback<int, double> ();
NS_ASSERT (two.IsNull ());
Invoking a null callback is just like invoking a null function pointer: it will
crash at runtime.
Bound Callbacks
***************
A very useful extension to the functor concept is that of a Bound Callback.
Previously it was mentioned that closures were originally function calls
packaged up for later execution. Notice that in all of the Callback
descriptions above, there is no way to package up any parameters for use
later -- when the ``Callback`` is called via ``operator()``. All of
the parameters are provided by the calling function.
What if it is desired to allow the client function (the one that provides the
callback) to provide some of the parameters? `Alexandrescu <http://erdani.com/book/main.html>`_ calls the process of
allowing a client to specify one of the parameters *"binding"*. One of the
parameters of ``operator()`` has been bound (fixed) by the client.
Some of our pcap tracing code provides a nice example of this. There is a
function that needs to be called whenever a packet is received. This function
calls an object that actually writes the packet to disk in the pcap file
format. The signature of one of these functions will be::
static void DefaultSink (Ptr<PcapFileWrapper> file, Ptr<const Packet> p);
The static keyword means this is a static function which does not need a
``this`` pointer, so it will be using C-style callbacks. We don't want the
calling code to have to know about anything but the Packet. What we want in
the calling code is just a call that looks like::
m_promiscSnifferTrace (m_currentPkt);
What we want to do is to *bind* the ``Ptr<PcapFileWriter> file`` to the
specific callback implementation when it is created and arrange for the
``operator()`` of the Callback to provide that parameter for free.
We provide the ``MakeBoundCallback`` template function for that purpose. It
takes the same parameters as the ``MakeCallback`` template function but also
takes the parameters to be bound. In the case of the example above::
MakeBoundCallback (&DefaultSink, file);
will create a specific callback implementation that knows to add in the extra
bound arguments. Conceptually, it extends the specific functor described above
with one or more bound arguments::
template <typename T, typename ARG, typename BOUND_ARG>
class SpecificFunctor : public Functor
{
public:
SpecificFunctor(T* p, int (T::*_pmi)(ARG arg), BOUND_ARG boundArg)
{
m_p = p;
m_pmi = pmi;
m_boundArg = boundArg;
}
virtual int operator() (ARG arg)
{
(*m_p.*m_pmi)(m_boundArg, arg);
}
private:
void (T::*m_pmi)(ARG arg);
T* m_p;
BOUND_ARG m_boundArg;
};
You can see that when the specific functor is created, the bound argument is saved
in the functor / callback object itself. When the ``operator()`` is invoked with
the single parameter, as in::
m_promiscSnifferTrace (m_currentPkt);
the implementation of ``operator()`` adds the bound parameter into the actual
function call::
(*m_p.*m_pmi)(m_boundArg, arg);
It's possible to bind two or three arguments as well. Say we have a function with
signature::
static void NotifyEvent (Ptr<A> a, Ptr<B> b, MyEventType e);
One can create bound callback binding first two arguments like::
MakeBoundCallback (&NotifyEvent, a1, b1);
assuming `a1` and `b1` are objects of type `A` and `B` respectively. Similarly for
three arguments one would have function with a signature::
static void NotifyEvent (Ptr<A> a, Ptr<B> b, MyEventType e);
Binding three arguments in done with::
MakeBoundCallback (&NotifyEvent, a1, b1, c1);
again assuming `a1`, `b1` and `c1` are objects of type `A`, `B` and `C` respectively.
This kind of binding can be used for exchanging information between objects in
simulation; specifically, bound callbacks can be used as traced callbacks, which will
be described in the next section.
Traced Callbacks
****************
*Placeholder subsection*
Callback locations in ns-3
**************************
Where are callbacks frequently used in |ns3|? Here are some of the
more visible ones to typical users:
* Socket API
* Layer-2/Layer-3 API
* Tracing subsystem
* API between IP and routing subsystems
Implementation details
**********************
The code snippets above are simplistic and only designed to illustrate the mechanism
itself. The actual Callback code is quite complicated and very template-intense and
a deep understanding of the code is not required. If interested, expert users may
find the following useful.
The code was originally written based on the techniques described in
`<http://www.codeproject.com/cpp/TTLFunction.asp>`_.
It was subsequently rewritten to follow the architecture outlined in
`Modern C++ Design, Generic Programming and Design Patterns Applied, Alexandrescu, chapter 5, Generalized Functors <http://www.moderncppdesign.com/book/main.html>`_.
This code uses:
* default template parameters to saves users from having to
specify empty parameters when the number of parameters
is smaller than the maximum supported number
* the pimpl idiom: the Callback class is passed around by
value and delegates the crux of the work to its pimpl pointer.
* two pimpl implementations which derive from CallbackImpl
FunctorCallbackImpl can be used with any functor-type
while MemPtrCallbackImpl can be used with pointers to
member functions.
* a reference list implementation to implement the Callback's
value semantics.
This code most notably departs from the Alexandrescu implementation in that it
does not use type lists to specify and pass around the types of the callback
arguments. Of course, it also does not use copy-destruction semantics and
relies on a reference list rather than autoPtr to hold the pointer.
-216
View File
@@ -1,216 +0,0 @@
# -*- coding: utf-8 -*-
#
# ns-3 documentation build configuration file, created by
# sphinx-quickstart on Tue Dec 14 09:00:39 2010.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.pngmath']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'ns-3'
copyright = u'2010, ns-3 project'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = 'ns-3-dev'
# The full version, including alpha/beta/rc tags.
release = 'ns-3-dev'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = []
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'ns3_html_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['../..']
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
html_title = 'Manual'
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = '%b %d, %Y %H:%M'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'ns-3doc'
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'ns-3-manual.tex', u'ns-3 Manual',
u'ns-3 project', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
latex_logo = '../../ns3_html_theme/static/ns-3.png'
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Additional stuff for the LaTeX preamble.
latex_preamble = '\usepackage{amssymb}'
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'ns-3-manual', u'ns-3 Manual',
[u'ns-3 project'], 1)
]
-647
View File
@@ -1,647 +0,0 @@
.. include:: replace.txt
.. highlight:: cpp
Creating Documentation
----------------------
|ns3| supplies two kinds of documentation: expository "user-guide"-style
chapters, and source code API documentation.
The "user-guide" chapters are written by hand in reStructuredText_
format (``.rst``), which is processed by the Python documentation
system Sphinx_ to generate web pages and pdf files.
The API documentation is generated from the source code itself,
using Doxygen_, to generate cross-linked web pages.
Both of these are important: the Sphinx chapters explain the *why*
and overview of using a model; the API documentation explains the
*how* details.
This chapter gives a quick overview of these
tools, emphasizing preferred usage and customizations for |ns3|.
To build all the standard documentation:
.. sourcecode:: bash
$ ./waf docs
For more specialized options, read on.
.. _reStructuredText: http://sphinx-doc.org/rest.html
.. _sphinx: http://sphinx-doc.org/
.. _doxygen: http://www.doxygen.org/
Documenting with Sphinx
***********************
We use Sphinx_ to generate expository chapters describing
the design and usage of each module. Right now you are reading the
:doc:`Documentation <documentation>` Chapter.
If you are reading the html version, the
`Show Source <_sources/documentation.txt>`_ link in the sidebar
will show you the reStructuredText source for this chapter.
Adding New Chapters
===================
Adding a new chapter takes three steps (described in more detail below):
#. Choose `Where?`_ the documentation file(s) will live.
#. `Link`_ from an existing page to the new documentation.
#. Add the new file to the `Makefile`_.
Where?
######
Documentation for a specific module, ``foo``, should normally go in
``src/foo/doc/``. For example ``src/foo/doc/foo.rst`` would be the
top-level document for the module. The ``src/create-module.py`` script
will create this file for you.
Some models require several ``.rst`` files, and figures; these should
all go in the ``src/foo/doc/`` directory. The docs are actually built
by a Sphinx Makefile. For especially involved
documentation, it may be helpful to have a local ``Makefile``
in the ``src/foo/doc/`` directory to
simplify building the documentation for this module
(`Antenna`_ is an example). Setting this up
is not particularly hard, but is beyond the scope of this chapter.
In some cases, documentation spans multiple models; the
`Network`_ chapter is an example. In these cases
adding the ``.rst`` files directly to ``doc/models/source/`` might
be appropriate.
.. _antenna: http://www.nsnam.org/docs/models/html/antenna.html
.. _network: http://www.nsnam.org/docs/models/html/network.html
Link
####
Sphinx has to know *where* your new chapter should appear. In most
cases, a new model chapter should appear the in `Models` book.
To add your chapter there, edit ``doc/models/source/index.rst``
.. sourcecode:: rest
.. toctree::
:maxdepth: 1
organization
animation
antenna
aodv
applications
...
Add the name of your document (without the ``.rst`` extension) to
this list. Please keep the Model chapters in alphabetical order,
to ease visual scanning for specific chapters.
Makefile
########
You also have to add your document to the appropriate ``Makefile``,
so ``make`` knows to check it for updates. The Models book Makefile
is ``doc/models/Makefile``, the Manual book Makefile is
``doc/manual/Makefile``.
.. sourcecode:: make
# list all model library .rst files that need to be copied to $SOURCETEMP
SOURCES = \
source/conf.py \
source/_static \
source/index.rst \
source/replace.txt \
source/organization.rst \
...
$(SRC)/antenna/doc/source/antenna.rst \
...
You add your ``.rst`` files to the ``SOURCES`` variable. To add figures,
read the comments in the ``Makefile`` to see which variable should contain
your image files. Again, please keep these in alphabetical order.
Building Sphinx Docs
====================
Building the Sphinx documentation is pretty simple.
To build all the Sphinx documentation:
.. sourcecode:: bash
$ ./waf sphinx
To build just the Models documentation:
.. sourcecode:: bash
$ make -C doc/models html
To see the generated documentation point your browser at
``doc/models/build/html``.
As you can see, Sphinx uses Make to guide the process.
The default target builds all enabled output forms, which in
|ns3| are the multi-page ``html``, single-page ``singlehtml``, and pdf
(``latex``). To build just the multi-page html, you add the ``html`` target:
.. sourcecode:: bash
$ make -C doc/models html
This can be helpful to reduce the build time (and the size of the
build chatter) as you are writing your chapter.
Before committing your documentation to the repo, please check
that it builds without errors or warnings. The build process
generates lots of output (mostly normal chatter from LaTeX),
which can make it difficult to see if there are any Sphinx
warnings or errors. To find important warnings and errors
build just the ``html`` version, then search the build log
for ``warning`` or ``error``.
|ns3| Specifics
===============
The Sphinx documentation_ and tutorial_ are pretty good. We won't duplicate
the basics here, instead focusing on preferred usage for |ns3|.
.. _documentation: http://sphinx-doc.org/contents.html
.. _tutorial: http://sphinx-doc.org/tutorial.html
* Start documents with these two lines:
.. sourcecode:: rest
.. include:: replace.txt
.. highlight:: cpp
The first line enables some simple replacements. For example,
typing ``|ns3|`` renders as |ns3|.
The second sets the default source code highlighting language explicitly
for the file, since the parser guess isn't always accurate.
(It's also possible to set the language explicitly for a single code block,
see below.)
* Sections:
Sphinx is pretty liberal about marking section headings. By convention,
we prefer this hierarchy:
.. sourcecode:: rest
.. heading hierarchy:
------------- Chapter
************* Section (#.#)
============= Subsection (#.#.#)
############# Sub-subsection
* Syntax Highlighting:
To use the default syntax highlighter, simply start a sourcecode block:
+--------------------------------------+------------------------------------+
| Sphinx Source | Rendered Output |
+======================================+====================================+
| .. sourcecode:: rest | |
| | |
| The ``Frobnitz`` is accessed by:: | The ``Frobnitz`` is accessed by:: |
| | |
| Foo::Frobnitz frob; | Foo::Frobnitz frob; |
| frob.Set (...); | frob.Set (...); |
+--------------------------------------+------------------------------------+
To use a specific syntax highlighter, for example, ``bash`` shell commands:
+--------------------------------------+------------------------------------+
| Sphinx Source | Rendered Output |
+======================================+====================================+
| .. sourcecode:: rest | |
| | |
| .. sourcecode:: bash | .. sourcecode:: bash |
| | |
| $ ls | $ ls |
+--------------------------------------+------------------------------------+
* Shorthand Notations:
These shorthands are defined:
+--------------------------------------+------------------------------------+
| Sphinx Source | Rendered Output |
+======================================+====================================+
| .. sourcecode:: rest | |
| | |
| |ns3| | |ns3| |
+--------------------------------------+------------------------------------+
| .. sourcecode:: rest | |
| | |
| |ns2| | |ns2| |
+--------------------------------------+------------------------------------+
| .. sourcecode:: rest | |
| | |
| |check| | |check| |
+--------------------------------------+------------------------------------+
| .. sourcecode:: rest | |
| | |
| :rfc:`6282` | :rfc:`6282` |
+--------------------------------------+------------------------------------+
Documenting with Doxygen
************************
We use Doxygen_ to generate browsable_ API documentation. Doxygen
provides a number of useful features:
* Summary table of all class members.
* Graphs of inheritance and collaboration for all classes.
* Links to the source code implementing each function.
* Links to every place a member is used.
* Links to every object used in implementing a function.
* Grouping of related classes, such as all the classes related to
a specific protocol.
In addition, we use the ``TypeId`` system to add to the documentation
for every class
* The ``Config`` paths by which such objects can be reached.
* Documentation for any ``Attributes``, including ``Attributes``
defined in parent classes.
* Documentation for any ``Trace`` sources defined by the class.
* The memory footprint for each class.
Doxygen operates by scaning the source code, looking for
specially marked comments. It also creates a cross reference,
indicating *where* each file, class, method, and variable is used.
.. _browsable: https://www.nsnam.org/docs/doxygen
Preferred Style
===============
The preferred style for Doxygen comments is the JavaDoc style::
/**
* Brief description of this class or method.
* Adjacent lines become a single paragraph.
*
* Longer description, with lots of details.
*
* Blank lines separate paragraphs.
*
* Explain what the class or method does, using what algorithm.
* Explain the units of arguments and return values.
*
* \note Note any limitations or gotchas.
*
* (For functions with arguments or return valued:)
* \param [in] foo Brief noun phrase describing this argument. Note
* that we indicate if the argument is input,
* output, or both.
* \param [in,out] bar Note Sentence case, and terminating period.
* \param [in] baz Indicate boolean values with \c true or \c false.
* \return Brief noun phrase describing the value.
*
* \internal
*
* You can also discuss internal implementation details.
* Understanding this material shouldn't be necessary to using
* the class or method.
*/
void ExampleFunction (const int foo, double & bar, const bool baz);
In this style the Doxygen comment block begins with two \`*' characters:
``/**``, and precedes the item being documented.
For items needing only a brief description, either of these short forms
is appropriate::
/** Destructor implementation. */
void DoDispose ();
int m_count; //!< Count of ...
Note the special form of the end of line comment, ``//!<``, indicating
that it refers to the *preceding* item.
Some items to note:
* Use sentence case, including the initial capital.
* Use punctuation, especially \`.'s at the end of sentences or phrases.
* The ``\brief`` tag is not needed; the first sentence will be
used as the brief description.
Every class, method, typedef, member variable, function argument
and return value should be documented in all source code files
which form the formal API and implementation for |ns3|, such as
``src/<module>/model/*``, ``src/<module>/helper/*`` and
``src/<module>/utils/*``. Documentation for items in ``src/<module>/test/*``
and ``src/<module>/examples/*`` is preferred, but not required.
Useful Features
===============
* Inherited members will automatically inherit docs from the parent,
(but can be replaced by local documentation).
#. Document the base class.
#. In the sub class mark inherited functions with an ordinary comment::
// Inherited methods
virtual void FooBar (void);
virtual int BarFoo (double baz);
Note that the signatures have to match exactly, so include the formal
argument ``(void)``
This doesn't work for static functions; see ``GetTypeId``, below, for an
example.
Building Doxygen Docs
=====================
Building the Doxygen documentation is pretty simple:
.. sourcecode:: bash
$ ./waf doxygen
This builds using the default configuration, which generates
documentation sections for *all* items, even if they do not have
explicit comment documentation blocks. This has the effect of
suppressing warnings for undocumented items, but makes sure everything
appears in the generated output, which is usually what you want for
general use. Note that we generate documentation even for modules
which are disabled, to make it easier to see all the features
available in |ns3|.
When writing documentation, it's often more useful to see which items
are generating warnings, typically about missing documentation. To
see the full warnings list, use the ``doc/doxygen.warnings.report.sh``
script:
.. sourcecode:: bash
$ doc/doxygen.warnings.report.sh
doxygen.warnings.report.sh:
Building and running print-introspected-doxygen...done.
Rebuilding doxygen (v1.8.10) docs with full errors...done.
Report of Doxygen warnings
----------------------------------------
(All counts are lower bounds.)
Warnings by module/directory:
Count Directory
----- ----------------------------------
3414 src/lte/model
1532 src/wimax/model
825 src/lte/test
....
1 src/applications/test
97 additional undocumented parameters.
----------------------------------------
12460 total warnings
100 directories with warnings
Warnings by file (alphabetical)
Count File
----- ----------------------------------
15 examples/routing/manet-routing-compare.cc
26 examples/stats/wifi-example-apps.h
12 examples/tutorial/fifth.cc
....
17 utils/python-unit-tests.py
----------------------------------------
771 files with warnings
Warnings by file (numerical)
Count File
----- ----------------------------------
273 src/lte/model/lte-rrc-sap.h
272 src/core/model/simulator.h
221 src/netanim/model/animation-interface.h
....
1 src/wimax/model/ul-job.cc
----------------------------------------
771 files with warnings
Doxygen Warnings Summary
----------------------------------------
100 directories
771 files
12460 warnings
(This snippet has *a lot* of lines suppressed!)
The script modifies the configuration to show all warnings, and to
shorten the run time. (It shortens the run time primarily by
disabling creation of diagrams, such as call trees, and doesn't
generate documentation for undocumented items, in order to trigger the
warnings.) As you can see, at this writing we have *a lot* of
undocumented items. The report summarizes warnings by module
``src/*/*``, and by file, in alphabetically and numerical order.
The script has a few options to pare things down and make the output more
manageable. For help, use the ``-h`` option. Having run it once
to do the Doxygen build and generate the full warnings log,
you can reprocess the log file with various "filters,"
without having to do the full Doxygen build again by
using the ``-s`` option. You can exclude warnings
from ``*/examples/*`` files (``-e`` option), and/or ``*/test/*`` files
(``-t``). Just to be clear, all of the filter options do the complete
fast doxygen build; they just filter doxygen log and warnings output.
Perhaps the most useful option when writing documentation comments
is ``-m <module>``, which will limit the report to just files matching
``src/<module>/*``, and follow the report with the actual warning lines.
Combine with ``-et`` and you can focus on the warnings that are most
urgent in a single module:
.. sourcecode:: bash
$ doc/doxygen.warnings.report.sh -m mesh/helper
...
Doxygen Warnings Summary
----------------------------------------
1 directories
3 files
149 warnings
Filtered Warnings
========================================
src/mesh/helper/dot11s/dot11s-installer.h:72: warning: Member m_root (variable) of class ns3::Dot11sStack is not documented.
src/mesh/helper/dot11s/dot11s-installer.h:35: warning: return type of member ns3::Dot11sStack::GetTypeId is not documented
src/mesh/helper/dot11s/dot11s-installer.h:56: warning: return type of member ns3::Dot11sStack::InstallStack is not documented
src/mesh/helper/flame/lfame-installer.h:40: warning: Member GetTypeId() (function) of class ns3::FlameStack is not documented.
src/mesh/helper/flame/flame-installer.h:60: warning: return type of member ns3::FlameStack::InstallStack is not documented
src/mesh/helper/mesh-helper.h:213: warning: Member m_nInterfaces (variable) of class ns3::MeshHelper is not documented.
src/mesh/helper/mesh-helper.h:214: warning: Member m_spreadChannelPolicy (variable) of class ns3::MeshHelper is not documented.
src/mesh/helper/mesh-helper.h:215: warning: Member m_stack (variable) of class ns3::MeshHelper is not documented.
src/mesh/helper/mesh-helper.h:216: warning: Member m_stackFactory (variable) of class ns3::MeshHelper is not documented.
src/mesh/helper/mesh-helper.h:209: warning: parameters of member ns3::MeshHelper::CreateInterface are not (all) documented
src/mesh/helper/mesh-helper.h:119: warning: parameters of member ns3::MeshHelper::SetStandard are not (all) documented
Finally, note that undocumented items (classes, methods, functions,
typedefs, *etc.* won't produce documentation when you build with
``doxygen.warnings.report.sh``, and only the outermost item
will produce a warning. As a result, if you don't see documentation
for a class method in the generated documentation, the class itself
probably needs documentation.
Now it's just a matter of understanding the code, and writing some
docs!
|ns3| Specifics
===============
As for Sphinx, the Doxygen docs_ and reference_ are pretty good.
We won't duplicate the basics here, instead focusing on preferred
usage for |ns3|.
.. _docs: http://www.stack.nl/~dimitri/doxygen/index.html
.. _reference: http://www.stack.nl/~dimitri/doxygen/manual/commands.html
* Use Doxygen ``Modules`` to group related items.
In the main header for a module, create a Doxgyen group::
/**
* \defgroup foo Foo protocol.
* Implemenation of the Foo protocol.
*/
The symbol ``foo`` is how other items can add themselves to this group.
The string following that will be the title for the group. Any futher
text will be the detailed description for the group page.
* Document each file, assigning it to the relevant group. In a header file::
/**
* \file
* \ingroup foo
* Class Foo declaration.
*/
or in the corresponding ``.cc`` file::
/**
* \file
* \ingroup foo
* Class FooBar implementation.
*/
* Mark each associated class as belonging to the group::
/**
* \ingroup foo
*
* FooBar packet type.
*/
class FooBar
* Did you know ``typedefs`` can have formal arguments? This enables
documentation of function pointer signatures::
/**
* Bar callback function signature.
*
* \param ale The size of a pint of ale, in Imperial ounces.
*/
typedef void (* BarCallback)(const int ale);
* Copy the ``Attribute`` help strings from the ``GetTypeId`` method to use
as the brief descriptions of associated members.
* ``\bugid{298}`` will create a link to bug 298 in our Bugzilla.
* ``\p foo`` in a description will format ``foo``
the same as the ``\param foo`` parameter, making it clear that you
are referring to an actual argument.
* ``\RFC{301}`` will create a link to RFC 301.
* Document the direction of function arguments with ``\param [in]``, *etc*.
The allowed values of the direction token are ``[in]``, ``[out]``, and
``[in,out]`` (note the explicit square brackets), as discussed in the
Doxygen docs for ``\param``.
* Document template arguments with ``\tparam``, just as you use ``\param``
for function arguments.
* For template arguments, indicate if they will be deduced or must be given
explicitly::
/**
* A templated function.
* \tparam T \explicit The return type.
* \tparam U \deduced The argument type.
* \param [in] a The argument.
*/
template <typename T, typename U> T Function (U a);
* Use ``\tparam U \deduced`` because the type ``U`` can be deduced at
the site where the template is invoked. Basically deduction can only
be done for function arguments.
* Use ``\tparam T \explicit`` because the type ``T`` can't be deduced;
it must be given explicitly at the invocation site.
* ``\internal`` should be used only to set off a discussion of implementation
details, not to mark ``private`` functions (they are already marked,
as ``private``!)
* Don't create classes with trivial names, such as ``class A``,
even in test suites. These cause all instances of the class name
literal \`A' to be rendered as links.
As noted above, static functions don't inherit the documentation
of the same functions in the parent class. |ns3| uses a few static
functions ubiquitously; the suggested documentation block for these
cases is:
* Default constructor/destructor::
MyClass (); //!< Default constructor
~MyClass (); //!< Destructor
* Dummy destructor and DoDispose::
/** Dummy destructor, see DoDispose. */
~MyClass ();
/** Destructor implementation */
virtual void DoDispose ();
* GetTypeId::
/**
* Register this type.
* \return The object TypeId.
*/
static TypeId GetTypeId (void);
-162
View File
@@ -1,162 +0,0 @@
.. include:: replace.txt
.. highlight:: bash
Enabling Subsets of |ns3| Modules
---------------------------------
As with most software projects, |ns3| is ever growing larger in terms of number of modules, lines of code, and memory footprint. Users, however, may only use a few of those modules at a time. For this reason, users may want to explicitly enable only the subset of the possible |ns3| modules that they actually need for their research.
This chapter discusses how to enable only the |ns3| modules that you are intersted in using.
How to enable a subset of |ns3|'s modules
*****************************************
If shared libraries are being built, then enabling a module will cause at least one library to be built:
.. sourcecode:: text
libns3-modulename.so
If the module has a test library and test libraries are being built, then
.. sourcecode:: text
libns3-modulename-test.so
will be built, too. Other modules that the module depends on and their test libraries will also be built.
By default, all modules are built in |ns3|. There are two ways to enable a subset of these modules:
#. Using waf's --enable-modules option
#. Using the |ns3| configuration file
Enable modules using waf's --enable-modules option
++++++++++++++++++++++++++++++++++++++++++++++++++
To enable only the core module with example and tests, for example,
try these commands: ::
$ ./waf clean
$ ./waf configure --enable-examples --enable-tests --enable-modules=core
$ ./waf build
$ cd build/debug/
$ ls
and the following libraries should be present:
.. sourcecode:: text
bindings libns3-core.so ns3 scratch utils
examples libns3-core-test.so samples src
Note the ``./waf clean`` step is done here only to make it more obvious which module libraries were built. You don't have to do ``./waf clean`` in order to enable subsets of modules.
Running test.py will cause only those tests that depend on module core to be run:
.. sourcecode:: text
24 of 24 tests passed (24 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
Repeat the above steps for the "network" module instead of the "core" module, and the following will be built, since network depends on core:
.. sourcecode:: text
bindings libns3-core.so libns3-network.so ns3 scratch utils
examples libns3-core-test.so libns3-network-test.so samples src
Running test.py will cause those tests that depend on only the core and network modules to be run:
.. sourcecode:: text
31 of 31 tests passed (31 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
Enable modules using the |ns3| configuration file
+++++++++++++++++++++++++++++++++++++++++++++++++
A configuration file, .ns3rc, has been added to |ns3| that allows users to specify which modules are to be included in the build.
When enabling a subset of |ns3| modules, the precedence rules are as follows:
#. the --enable-modules configure string overrides any .ns3rc file
#. the .ns3rc file in the top level |ns3| directory is next consulted, if present
#. the system searches for ~/.ns3rc if the above two are unspecified
If none of the above limits the modules to be built, all modules that waf knows about will be built.
The maintained version of the .ns3rc file in the |ns3| source code repository resides in the ``utils`` directory. The reason for this is if it were in the top-level directory of the repository, it would be prone to accidental checkins from maintainers that enable the modules they want to use. Therefore, users need to manually copy the .ns3rc from the ``utils`` directory to their preferred place (top level directory or their home directory) to enable persistent modular build configuration.
Assuming that you are in the top level |ns3| directory, you can get a copy of the .ns3rc file that is in the ``utils`` directory as follows: ::
$ cp utils/.ns3rc .
The .ns3rc file should now be in your top level |ns3| directory, and it contains the following:
.. sourcecode:: python
#! /usr/bin/env python
# A list of the modules that will be enabled when ns-3 is run.
# Modules that depend on the listed modules will be enabled also.
#
# All modules can be enabled by choosing 'all_modules'.
modules_enabled = ['all_modules']
# Set this equal to true if you want examples to be run.
examples_enabled = False
# Set this equal to true if you want tests to be run.
tests_enabled = False
Use your favorite editor to modify the .ns3rc file to only enable the core module with examples and tests like this:
.. sourcecode:: python
#! /usr/bin/env python
# A list of the modules that will be enabled when ns-3 is run.
# Modules that depend on the listed modules will be enabled also.
#
# All modules can be enabled by choosing 'all_modules'.
modules_enabled = ['core']
# Set this equal to true if you want examples to be run.
examples_enabled = True
# Set this equal to true if you want tests to be run.
tests_enabled = True
Only the core module will be enabled now if you try these commands: ::
$ ./waf clean
$ ./waf configure
$ ./waf build
$ cd build/debug/
$ ls
and the following libraries should be present:
.. sourcecode:: text
bindings libns3-core.so ns3 scratch utils
examples libns3-core-test.so samples src
Note the ``./waf clean`` step is done here only to make it more obvious which module libraries were built. You don't have to do ``./waf clean`` in order to enable subsets of modules.
Running test.py will cause only those tests that depend on module core to be run:
.. sourcecode:: text
24 of 24 tests passed (24 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
Repeat the above steps for the "network" module instead of the "core" module, and the following will be built, since network depends on core:
.. sourcecode:: text
bindings libns3-core.so libns3-network.so ns3 scratch utils
examples libns3-core-test.so libns3-network-test.so samples src
Running test.py will cause those tests that depend on only the core and network modules to be run:
.. sourcecode:: text
31 of 31 tests passed (31 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
-159
View File
@@ -1,159 +0,0 @@
.. include:: replace.txt
.. highlight:: bash
Enabling/disabling |ns3| Tests and Examples
-------------------------------------------
The |ns3| distribution includes many examples and tests that are used to validate the |ns3| system. Users, however, may not always want these examples and tests to be run for their installation of |ns3|.
This chapter discusses how to build |ns3| with or without its examples and tests.
How to enable/disable examples and tests in |ns3|
*************************************************
There are 3 ways to enable/disable examples and tests in |ns3|:
#. Using build.py when |ns3| is built for the first time
#. Using waf once |ns3| has been built
#. Using the |ns3| configuration file once |ns3| has been built
Enable/disable examples and tests using build.py
++++++++++++++++++++++++++++++++++++++++++++++++
You can use build.py to enable/disable examples and tests when |ns3| is built for the first time.
By default, examples and tests are not built in |ns3|.
From the ns-3-allinone directory, you can build |ns3| without any
examples or tests simply by doing: ::
$ ./build.py
Running test.py in the top level |ns3| directory now will cause no examples or tests to be run:
.. sourcecode:: text
0 of 0 tests passed (0 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
If you would like build |ns3| with examples and tests, then do the following from the ns-3-allinone directory: ::
$ ./build.py --enable-examples --enable-tests
Running test.py in the top level |ns3| directory will cause all of the examples and tests to be run:
.. sourcecode:: text
170 of 170 tests passed (170 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
Enable/disable examples and tests using waf
+++++++++++++++++++++++++++++++++++++++++++
You can use waf to enable/disable examples and tests once |ns3| has been built.
By default, examples and tests are not built in |ns3|.
From the top level |ns3| directory, you can build |ns3| without any
examples or tests simply by doing: ::
$ ./waf configure
$ ./waf build
Running test.py now will cause no examples or tests to be run:
.. sourcecode:: text
0 of 0 tests passed (0 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
If you would like build |ns3| with examples and tests, then do the following from the top level |ns3| directory: ::
$ ./waf configure --enable-examples --enable-tests
$ ./waf build
Running test.py will cause all of the examples and tests to be run:
.. sourcecode:: text
170 of 170 tests passed (170 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
Enable/disable examples and tests using the |ns3| configuration file
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A configuration file, .ns3rc, has been added to |ns3| that allows users to specify whether examples and tests should be built or not. You can use this file to enable/disable examples and tests once |ns3| has been built.
When enabling disabling examples and tests, the precedence rules are as follows:
#. the --enable-examples/--disable-examples configure strings override any .ns3rc file
#. the --enable-tests/--disable-tests configure strings override any .ns3rc file
#. the .ns3rc file in the top level |ns3| directory is next consulted, if present
#. the system searches for ~/.ns3rc if the .ns3rc file was not found in the previous step
If none of the above exists, then examples and tests will not be built.
The maintained version of the .ns3rc file in the |ns3| source code repository resides in the ``utils`` directory. The reason for this is if it were in the top-level directory of the repository, it would be prone to accidental checkins from maintainers that enable the modules they want to use. Therefore, users need to manually copy the .ns3rc from the ``utils`` directory to their preferred place (top level directory or their home directory) to enable persistent enabling of examples and tests.
Assuming that you are in the top level |ns3| directory, you can get a copy of the .ns3rc file that is in the ``utils`` directory as follows: ::
$ cp utils/.ns3rc .
The .ns3rc file should now be in your top level |ns3| directory, and it contains the following:
.. sourcecode:: python
#! /usr/bin/env python
# A list of the modules that will be enabled when ns-3 is run.
# Modules that depend on the listed modules will be enabled also.
#
# All modules can be enabled by choosing 'all_modules'.
modules_enabled = ['all_modules']
# Set this equal to true if you want examples to be run.
examples_enabled = False
# Set this equal to true if you want tests to be run.
tests_enabled = False
From the top level |ns3| directory, you can build |ns3| without any
examples or tests simply by doing: ::
$ ./waf configure
$ ./waf build
Running test.py now will cause no examples or tests to be run:
.. sourcecode:: text
0 of 0 tests passed (0 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
If you would like build |ns3| with examples and tests, use your
favorite editor to change the values in the .ns3rc file for
examples_enabled and tests_enabled file to be True:
.. sourcecode:: python
#! /usr/bin/env python
# A list of the modules that will be enabled when ns-3 is run.
# Modules that depend on the listed modules will be enabled also.
#
# All modules can be enabled by choosing 'all_modules'.
modules_enabled = ['all_modules']
# Set this equal to true if you want examples to be run.
examples_enabled = True
# Set this equal to true if you want tests to be run.
tests_enabled = True
From the top level |ns3| directory, you can build |ns3| with examples
and tests simply by doing: ::
$ ./waf configure
$ ./waf build
Running test.py will cause all of the examples and tests to be run:
.. sourcecode:: text
170 of 170 tests passed (170 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
-201
View File
@@ -1,201 +0,0 @@
.. include:: replace.txt
.. highlight:: cpp
.. heading hierarchy:
------------- Chapter
************* Section (#.#)
============= Subsection (#.#.#)
############# Paragraph (no number)
Events and Simulator
--------------------
|ns3| is a discrete-event network simulator. Conceptually, the simulator
keeps track of a number of events that are scheduled to execute at a
specified simulation time. The job of the simulator is to execute the
events in sequential time order. Once the completion of an event occurs,
the simulator will move to the next event (or will exit if there are no
more events in the event queue). If, for example, an event scheduled
for simulation time "100 seconds" is executed, and the next event is not
scheduled until "200 seconds", the simulator will immediately jump from
100 seconds to 200 seconds (of simulation time) to execute the next event.
This is what is meant by "discrete-event" simulator.
To make this all happen, the simulator needs a few things:
1) a simulator object that can access an event queue where events are
stored and that can manage the execution of events
2) a scheduler responsible for inserting and removing events from the queue
3) a way to represent simulation time
4) the events themselves
This chapter of the manual describes these fundamental objects
(simulator, scheduler, time, event) and how they are used.
Event
*****
*To be completed*
Simulator
*********
The Simulator class is the public entry point to access event scheduling
facilities. Once a couple of events have been scheduled to start the
simulation, the user can start to execute them by entering the simulator
main loop (call ``Simulator::Run``). Once the main loop starts running, it
will sequentially execute all scheduled events in order from oldest to
most recent until there are either no more events left in the event
queue or Simulator::Stop has been called.
To schedule events for execution by the simulator main loop, the
Simulator class provides the Simulator::Schedule* family of functions.
1) Handling event handlers with different signatures
These functions are declared and implemented as C++ templates to handle
automatically the wide variety of C++ event handler signatures used in
the wild. For example, to schedule an event to execute 10 seconds in the
future, and invoke a C++ method or function with specific arguments, you
might write this:
::
void handler (int arg0, int arg1)
{
std::cout << "handler called with argument arg0=" << arg0 << " and
arg1=" << arg1 << std::endl;
}
Simulator::Schedule(Seconds(10), &handler, 10, 5);
Which will output:
.. sourcecode:: text
handler called with argument arg0=10 and arg1=5
Of course, these C++ templates can also handle transparently member
methods on C++ objects:
*To be completed: member method example*
Notes:
* the ns-3 Schedule methods recognize automatically functions and
methods only if they take less than 5 arguments. If you need them to
support more arguments, please, file a bug report.
* Readers familiar with the term 'fully-bound functors' will recognize
the Simulator::Schedule methods as a way to automatically construct such
objects.
2) Common scheduling operations
The Simulator API was designed to make it really simple to schedule most
events. It provides three variants to do so (ordered from most commonly
used to least commonly used):
* Schedule methods which allow you to schedule an event in the future
by providing the delay between the current simulation time and the
expiration date of the target event.
* ScheduleNow methods which allow you to schedule an event for the
current simulation time: they will execute _after_ the current event is
finished executing but _before_ the simulation time is changed for the
next event.
* ScheduleDestroy methods which allow you to hook in the shutdown
process of the Simulator to cleanup simulation resources: every
'destroy' event is executed when the user calls the Simulator::Destroy
method.
3) Maintaining the simulation context
There are two basic ways to schedule events, with and without *context*.
What does this mean?
::
Simulator::Schedule (Time const &time, MEM mem_ptr, OBJ obj);
vs.
::
Simulator::ScheduleWithContext (uint32_t context, Time const &time, MEM mem_ptr, OBJ obj);
Readers who invest time and effort in developing or using a non-trivial
simulation model will know the value of the ns-3 logging framework to
debug simple and complex simulations alike. One of the important
features that is provided by this logging framework is the automatic
display of the network node id associated with the 'currently' running
event.
The node id of the currently executing network node is in fact tracked
by the Simulator class. It can be accessed with the
Simulator::GetContext method which returns the 'context' (a 32-bit
integer) associated and stored in the currently-executing event. In some
rare cases, when an event is not associated with a specific network
node, its 'context' is set to 0xffffffff.
To associate a context to each event, the Schedule, and ScheduleNow
methods automatically reuse the context of the currently-executing event
as the context of the event scheduled for execution later.
In some cases, most notably when simulating the transmission of a packet
from a node to another, this behavior is undesirable since the expected
context of the reception event is that of the receiving node, not the
sending node. To avoid this problem, the Simulator class provides a
specific schedule method: ScheduleWithContext which allows one to
provide explicitly the node id of the receiving node associated with
the receive event.
*XXX: code example*
In some very rare cases, developers might need to modify or understand
how the context (node id) of the first event is set to that of its
associated node. This is accomplished by the NodeList class: whenever a
new node is created, the NodeList class uses ScheduleWithContext to
schedule a 'initialize' event for this node. The 'initialize' event thus executes
with a context set to that of the node id and can use the normal variety
of Schedule methods. It invokes the Node::Initialize method which propagates
the 'initialize' event by calling the DoInitialize method for each object
associated with the node. The DoInitialize method overridden in some of these
objects (most notably in the Application base class) will schedule some
events (most notably Application::StartApplication) which will in turn
scheduling traffic generation events which will in turn schedule
network-level events.
Notes:
* Users need to be careful to propagate DoInitialize methods across objects
by calling Initialize explicitely on their member objects
* The context id associated with each ScheduleWithContext method has
other uses beyond logging: it is used by an experimental branch of ns-3
to perform parallel simulation on multicore systems using
multithreading.
The Simulator::* functions do not know what the context is: they
merely make sure that whatever context you specify with
ScheduleWithContext is available when the corresponding event executes
with ::GetContext.
It is up to the models implemented on top of Simulator::* to interpret
the context value. In ns-3, the network models interpret the context
as the node id of the node which generated an event. This is why it is
important to call ScheduleWithContext in ns3::Channel subclasses
because we are generating an event from node i to node j and we want
to make sure that the event which will run on node j has the right
context.
Time
****
*To be completed*
Scheduler
*********
*To be completed*
-1
View File
@@ -1 +0,0 @@
../figures
-307
View File
@@ -1,307 +0,0 @@
.. include:: replace.txt
.. highlight:: cpp
Making Plots using the Gnuplot Class
------------------------------------
There are 2 common methods to make a plot using |ns3| and gnuplot (http://www.gnuplot.info):
#. Create a gnuplot control file using |ns3|'s Gnuplot class.
#. Create a gnuplot data file using values generated by |ns3|.
This section is about method 1, i.e. it is about how to make a plot using |ns3|'s Gnuplot class. If you are interested in method 2, see the "A Real Example" subsection under the "Tracing" section in the |ns3| `Tutorial <http://www.nsnam.org/tutorials.html>`_.
Creating Plots Using the Gnuplot Class
**************************************
The following steps must be taken in order to create a plot using |ns3|'s Gnuplot class:
#. Modify your code so that is uses the Gnuplot class and its functions.
#. Run your code so that it creates a gnuplot control file.
#. Call gnuplot with the name of the gnuplot control file.
#. View the graphics file that was produced in your favorite graphics viewer.
See the code from the example plots that are discussed below for details on step 1.
An Example Program that Uses the Gnuplot Class
**********************************************
An example program that uses |ns3|'s Gnuplot class can be found here:
.. sourcecode:: bash
src/stats/examples/gnuplot-example.cc
In order to run this example, do the following:
.. sourcecode:: bash
$ ./waf shell
$ cd build/debug/src/stats/examples
$ ./gnuplot-example
This should produce the following gnuplot control files in the directory where the example is located:
.. sourcecode:: text
plot-2d.plt
plot-2d-with-error-bars.plt
plot-3d.plt
In order to process these gnuplot control files, do the following:
.. sourcecode:: bash
$ gnuplot plot-2d.plt
$ gnuplot plot-2d-with-error-bars.plt
$ gnuplot plot-3d.plt
This should produce the following graphics files in the directory where the example is located:
.. sourcecode:: text
plot-2d.png
plot-2d-with-error-bars.png
plot-3d.png
You can view these graphics files in your favorite graphics viewer. If you have gimp installed on your machine, for example, you can do this:
.. sourcecode:: bash
$ gimp plot-2d.png
$ gimp plot-2d-with-error-bars.png
$ gimp plot-3d.png
An Example 2-Dimensional Plot
*****************************
The following 2-Dimensional plot
.. _plot-2d:
.. figure:: figures/plot-2d.*
was created using the following code from gnuplot-example.cc: ::
using namespace std;
string fileNameWithNoExtension = "plot-2d";
string graphicsFileName = fileNameWithNoExtension + ".png";
string plotFileName = fileNameWithNoExtension + ".plt";
string plotTitle = "2-D Plot";
string dataTitle = "2-D Data";
// Instantiate the plot and set its title.
Gnuplot plot (graphicsFileName);
plot.SetTitle (plotTitle);
// Make the graphics file, which the plot file will create when it
// is used with Gnuplot, be a PNG file.
plot.SetTerminal ("png");
// Set the labels for each axis.
plot.SetLegend ("X Values", "Y Values");
// Set the range for the x axis.
plot.AppendExtra ("set xrange [-6:+6]");
// Instantiate the dataset, set its title, and make the points be
// plotted along with connecting lines.
Gnuplot2dDataset dataset;
dataset.SetTitle (dataTitle);
dataset.SetStyle (Gnuplot2dDataset::LINES_POINTS);
double x;
double y;
// Create the 2-D dataset.
for (x = -5.0; x <= +5.0; x += 1.0)
{
// Calculate the 2-D curve
//
// 2
// y = x .
//
y = x * x;
// Add this point.
dataset.Add (x, y);
}
// Add the dataset to the plot.
plot.AddDataset (dataset);
// Open the plot file.
ofstream plotFile (plotFileName.c_str());
// Write the plot file.
plot.GenerateOutput (plotFile);
// Close the plot file.
plotFile.close ();
An Example 2-Dimensional Plot with Error Bars
*********************************************
The following 2-Dimensional plot with error bars in the x and y directions
.. _plot-2d-with-error-bars:
.. figure:: figures/plot-2d-with-error-bars.*
was created using the following code from gnuplot-example.cc: ::
using namespace std;
string fileNameWithNoExtension = "plot-2d-with-error-bars";
string graphicsFileName = fileNameWithNoExtension + ".png";
string plotFileName = fileNameWithNoExtension + ".plt";
string plotTitle = "2-D Plot With Error Bars";
string dataTitle = "2-D Data With Error Bars";
// Instantiate the plot and set its title.
Gnuplot plot (graphicsFileName);
plot.SetTitle (plotTitle);
// Make the graphics file, which the plot file will create when it
// is used with Gnuplot, be a PNG file.
plot.SetTerminal ("png");
// Set the labels for each axis.
plot.SetLegend ("X Values", "Y Values");
// Set the range for the x axis.
plot.AppendExtra ("set xrange [-6:+6]");
// Instantiate the dataset, set its title, and make the points be
// plotted with no connecting lines.
Gnuplot2dDataset dataset;
dataset.SetTitle (dataTitle);
dataset.SetStyle (Gnuplot2dDataset::POINTS);
// Make the dataset have error bars in both the x and y directions.
dataset.SetErrorBars (Gnuplot2dDataset::XY);
double x;
double xErrorDelta;
double y;
double yErrorDelta;
// Create the 2-D dataset.
for (x = -5.0; x <= +5.0; x += 1.0)
{
// Calculate the 2-D curve
//
// 2
// y = x .
//
y = x * x;
// Make the uncertainty in the x direction be constant and make
// the uncertainty in the y direction be a constant fraction of
// y's value.
xErrorDelta = 0.25;
yErrorDelta = 0.1 * y;
// Add this point with uncertainties in both the x and y
// direction.
dataset.Add (x, y, xErrorDelta, yErrorDelta);
}
// Add the dataset to the plot.
plot.AddDataset (dataset);
// Open the plot file.
ofstream plotFile (plotFileName.c_str());
// Write the plot file.
plot.GenerateOutput (plotFile);
// Close the plot file.
plotFile.close ();
An Example 3-Dimensional Plot
*****************************
The following 3-Dimensional plot
.. _plot-3d:
.. figure:: figures/plot-3d.*
was created using the following code from gnuplot-example.cc: ::
using namespace std;
string fileNameWithNoExtension = "plot-3d";
string graphicsFileName = fileNameWithNoExtension + ".png";
string plotFileName = fileNameWithNoExtension + ".plt";
string plotTitle = "3-D Plot";
string dataTitle = "3-D Data";
// Instantiate the plot and set its title.
Gnuplot plot (graphicsFileName);
plot.SetTitle (plotTitle);
// Make the graphics file, which the plot file will create when it
// is used with Gnuplot, be a PNG file.
plot.SetTerminal ("png");
// Rotate the plot 30 degrees around the x axis and then rotate the
// plot 120 degrees around the new z axis.
plot.AppendExtra ("set view 30, 120, 1.0, 1.0");
// Make the zero for the z-axis be in the x-axis and y-axis plane.
plot.AppendExtra ("set ticslevel 0");
// Set the labels for each axis.
plot.AppendExtra ("set xlabel 'X Values'");
plot.AppendExtra ("set ylabel 'Y Values'");
plot.AppendExtra ("set zlabel 'Z Values'");
// Set the ranges for the x and y axis.
plot.AppendExtra ("set xrange [-5:+5]");
plot.AppendExtra ("set yrange [-5:+5]");
// Instantiate the dataset, set its title, and make the points be
// connected by lines.
Gnuplot3dDataset dataset;
dataset.SetTitle (dataTitle);
dataset.SetStyle ("with lines");
double x;
double y;
double z;
// Create the 3-D dataset.
for (x = -5.0; x <= +5.0; x += 1.0)
{
for (y = -5.0; y <= +5.0; y += 1.0)
{
// Calculate the 3-D surface
//
// 2 2
// z = x * y .
//
z = x * x * y * y;
// Add this point.
dataset.Add (x, y, z);
}
// The blank line is necessary at the end of each x value's data
// points for the 3-D surface grid to work.
dataset.AddEmptyLine ();
}
// Add the dataset to the plot.
plot.AddDataset (dataset);
// Open the plot file.
ofstream plotFile (plotFileName.c_str());
// Write the plot file.
plot.GenerateOutput (plotFile);
// Close the plot file.
plotFile.close ();
-118
View File
@@ -1,118 +0,0 @@
.. include:: replace.txt
.. highlight:: cpp
Hash Functions
----------------
|ns3| provides a generic interface to general purpose hash functions.
In the simplest usage, the hash function returns the 32-bit or 64-bit
hash of a data buffer or string. The default underlying hash function
is murmur3_, chosen because it has good hash function properties and
offers a 64-bit version. The venerable FNV1a_ hash is also available.
There is a straight-forward mechanism to
add (or provide at run time) alternative hash function implementations.
.. _murmur3: http://code.google.com/p/smhasher/wiki/MurmurHash3
.. _FNV1a: http://isthe.com/chongo/tech/comp/fnv/
Basic Usage
***********
The simplest way to get a hash value of a data buffer or string is just::
#include "ns3/hash.h"
using namespace ns3;
char * buffer = ...
size_t buffer_size = ...
uint32_t buffer_hash = Hash32 ( buffer, buffer_size);
std::string s;
uint32_t string_hash = Hash32 (s);
Equivalent functions are defined for 64-bit hash values.
Incremental Hashing
*******************
In some situations it's useful to compute the hash of multiple buffers,
as if they had been joined together. (For example, you might want
the hash of a packet stream, but not want to assemble a single buffer
with the combined contents of all the packets.)
This is almost as straight-forward as the first example::
#include "ns3/hash.h"
using namespace ns3;
char * buffer;
size_t buffer_size;
Hasher hasher; // Use default hash function
for (<every buffer>)
{
buffer = get_next_buffer ();
hasher (buffer, buffer_size);
}
uint32_t combined_hash = hasher.GetHash32 ();
By default ``Hasher`` preserves internal state to enable incremental
hashing. If you want to reuse a ``Hasher`` object (for example
because it's configured with a non-default hash function), but don't
want to add to the previously computed hash, you need to ``clear()``
first::
hasher.clear ().GetHash32 (buffer, buffer_size);
This reinitializes the internal state before hashing the buffer.
Using an Alternative Hash Function
**********************************
The default hash function is murmur3_. FNV1a_ is also available. To specify
the hash function explicitly, use this contructor::
Hasher hasher = Hasher ( Create<Hash::Function::Fnv1a> () );
Adding New Hash Function Implementations
****************************************
To add the hash function ``foo``, follow the ``hash-murmur3.h``/``.cc`` pattern:
* Create a class declaration (``.h``) and definition (``.cc``) inheriting
from ``Hash::Implementation``.
* ``include`` the declaration in ``hash.h`` (at the point where
``hash-murmur3.h`` is included.
* In your own code, instantiate a ``Hasher`` object via the constructor
``Hasher (Ptr<Hash::Function::Foo> ())``
If your hash function is a single function, e.g. ``hashf``, you don't
even need to create a new class derived from HashImplementation::
Hasher hasher =
Hasher ( Create<Hash::Function::Hash32> (&hashf) );
For this to compile, your ``hashf`` has to match one of the function pointer
signatures::
typedef uint32_t (*Hash32Function_ptr) (const char *, const size_t);
typedef uint64_t (*Hash64Function_ptr) (const char *, const size_t);
Sources for Hash Functions
**************************
Sources for other hash function implementations include:
* Peter Kankowski: http://www.strchr.com
* Arash Partow: http://www.partow.net/programming/hashfunctions/index.html
* SMHasher: http://code.google.com/p/smhasher/
* Sanmayce: http://www.sanmayce.com/Fastest_Hash/index.html
-38
View File
@@ -1,38 +0,0 @@
.. include:: replace.txt
.. highlight:: cpp
Helpers
-------
The above chapters introduced you to various |ns3| programming concepts such as
smart pointers for reference-counted memory management, attributes, namespaces,
callbacks, etc. Users who work at this low-level API can interconnect |ns3|
objects with fine granulariy. However, a simulation program written entirely
using the low-level API would be quite long and tedious to code. For this
reason, a separate so-called "helper API" has been overlaid on the core |ns3|
API. If you have read the |ns3| tutorial, you will already be familiar with the
helper API, since it is the API that new users are typically introduced to
first. In this chapter, we introduce the design philosophy of the helper API
and contrast it to the low-level API. If you become a heavy user of |ns3|, you
will likely move back and forth between these APIs even in the same program.
The helper API has a few goals:
#. the rest of ``src/`` has no dependencies on the helper API; anything that can
be done with the helper API can be coded also at the low-level API
#. **Containers:** Often simulations will need to do a number of identical
actions to groups of objects. The helper API makes heavy use of containers of
similar objects to which similar or identical operations can be performed.
#. The helper API is not generic; it does not strive to maximize code reuse. So,
programming constructs such as polymorphism and templates that achieve code
reuse are not as prevalent. For instance, there are separate CsmaNetDevice
helpers and PointToPointNetDevice helpers but they do not derive from a
common NetDevice base class.
#. The helper API typically works with stack-allocated (vs. heap-allocated)
objects. For some programs, |ns3| users may not need to worry about any low
level Object Create or Ptr handling; they can make do with containers of
objects and stack-allocated helpers that operate on them.
The helper API is really all about making |ns3| programs easier to write and
read, without taking away the power of the low-level interface. The rest of this
chapter provides some examples of the programming conventions of the helper API.
-143
View File
@@ -1,143 +0,0 @@
.. include:: replace.txt
.. highlight:: cpp
How to write tests
------------------
A primary goal of the ns-3 project is to help users to improve the
validity and credibility of their results. There are many elements
to obtaining valid models and simulations, and testing is a major
component. If you contribute models or examples to ns-3, you may
be asked to contribute test code. Models that you contribute will be
used for many years by other people, who probably have no idea upon
first glance whether the model is correct. The test code that you
write for your model will help to avoid future regressions in
the output and will aid future users in understanding the verification
and bounds of applicability of your models.
There are many ways to verify the correctness of a model's implementation.
In this section,
we hope to cover some common cases that can be used as a guide to
writing new tests.
Sample TestSuite skeleton
*************************
When starting from scratch (i.e. not adding a TestCase to an existing
TestSuite), these things need to be decided up front:
* What the test suite will be called
* What type of test it will be (Build Verification Test, Unit Test,
System Test, or Performance Test)
* Where the test code will live (either in an existing ns-3 module or
separately in src/test/ directory). You will have to edit the wscript
file in that directory to compile your new code, if it is a new file.
A program called ``src/create-module.py`` is a good starting point.
This program can be invoked such as ``create-module.py router`` for
a hypothetical new module called ``router``. Once you do this, you
will see a ``router`` directory, and a ``test/router-test-suite.cc``
test suite. This file can be a starting point for your initial test.
This is a working test suite, although the actual tests performed are
trivial. Copy it over to your module's test directory, and do a global
substitution of "Router" in that file for something pertaining to
the model that you want to test. You can also edit things such as a
more descriptive test case name.
You also need to add a block into your wscript to get this test to
compile:
.. sourcecode:: python
module_test.source = [
'test/router-test-suite.cc',
]
Before you actually start making this do useful things, it may help
to try to run the skeleton. Make sure that ns-3 has been configured with
the "--enable-tests" option. Let's assume that your new test suite
is called "router" such as here:
::
RouterTestSuite::RouterTestSuite ()
: TestSuite ("router", UNIT)
Try this command:
.. sourcecode:: bash
$ ./test.py -s router
Output such as below should be produced:
.. sourcecode:: text
PASS: TestSuite router
1 of 1 tests passed (1 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
See src/lte/test/test-lte-antenna.cc for a worked example.
Test macros
***********
There are a number of macros available for checking test program
output with expected output. These macros are defined in
``src/core/model/test.h``.
The main set of macros that are used include the following:
::
NS_TEST_ASSERT_MSG_EQ(actual, limit, msg)
NS_TEST_ASSERT_MSG_NE(actual, limit, msg)
NS_TEST_ASSERT_MSG_LT(actual, limit, msg)
NS_TEST_ASSERT_MSG_GT(actual, limit, msg)
NS_TEST_ASSERT_MSG_EQ_TOL(actual, limit, tol, msg)
The first argument ``actual`` is the value under test, the second value
``limit`` is the expected value (or the value to test against), and the
last argument ``msg`` is the error message to print out if the test fails.
The first four macros above test for equality, inequality, less than, or
greater than, respectively. The fifth macro above tests for equality,
but within a certain tolerance. This variant is useful when testing
floating point numbers for equality against a limit, where you want to
avoid a test failure due to rounding errors.
Finally, there are variants of the above where the keyword ``ASSERT``
is replaced by ``EXPECT``. These variants are designed specially for
use in methods (especially callbacks) returning void. Reserve their
use for callbacks that you use in your test programs; otherwise,
use the ``ASSERT`` variants.
How to add an example program to the test suite
***********************************************
One can "smoke test" that examples compile and run successfully
to completion (without memory leaks) using the ``examples-to-run.py``
script located in your module's test directory. Briefly, by including
an instance of this file in your test directory, you can cause the
test runner to execute the examples listed. It is usually best to make
sure that you select examples that have reasonably short run times so as
to not bog down the tests. See the example in ``src/lte/test/``
directory.
Testing for boolean outcomes
****************************
Testing outcomes when randomness is involved
********************************************
Testing output data against a known distribution
************************************************
Providing non-trivial input vectors of data
*******************************************
Storing and referencing non-trivial output data
***********************************************
Presenting your output test data
********************************
-43
View File
@@ -1,43 +0,0 @@
.. only:: html or latex
ns-3 Manual
===========
This is the *ns-3 Manual*. Primary documentation for the ns-3 project is
available in five forms:
* `ns-3 Doxygen <http://www.nsnam.org/doxygen/index.html>`_: Documentation of the public APIs of the simulator
* Tutorial, Manual *(this document)*, and Model Library for the `latest release <http://www.nsnam.org/documentation/latest/>`_ and `development tree <http://www.nsnam.org/ns-3-dev/documentation/>`_
* `ns-3 wiki <http://www.nsnam.org/wiki/Main_Page>`_
Contents
--------
.. toctree::
:maxdepth: 2
organization
random-variables
hash-functions
events
callbacks
object-model
attributes
object-names
logging
tracing
data-collection
statistics
realtime
helpers
gnuplot
python
tests
support
Source
------
This document is written in `reStructuredText <http://docutils.sourceforge.net/rst.html>`_ for `Sphinx <http://sphinx.pocoo.org/>`_ and is maintained in the
``doc/manual`` directory of ns-3's source code.
-429
View File
@@ -1,429 +0,0 @@
.. include:: replace.txt
.. highlight:: cpp
.. heading hierarchy:
------------- Chapter
************* Section (#.#)
============= Subsection (#.#.#)
############# Paragraph (no number)
Logging
-------
The |ns3| logging facility can be used to monitor or debug the progress
of simulation programs. Logging output can be enabled by program statements
in your ``main()`` program or by the use of the ``NS_LOG`` environment variable.
Logging statements are not compiled into optimized builds of |ns3|. To use
logging, one must build the (default) debug build of |ns3|.
The project makes no guarantee about whether logging output will remain
the same over time. Users are cautioned against building simulation output
frameworks on top of logging code, as the output and the way the output
is enabled may change over time.
Overview
********
|ns3| logging statements are typically used to log various program
execution events, such as the occurrence of simulation events or the
use of a particular function.
For example, this code snippet is from ``Ipv4L3Protocol::IsDestinationAddress()``::
if (address == iaddr.GetBroadcast ())
{
NS_LOG_LOGIC ("For me (interface broadcast address)");
return true;
}
If logging has been enabled for the ``Ipv4L3Protocol`` component at a severity
of ``LOGIC`` or above (see below about log severity), the statement
will be printed out; otherwise, it will be suppressed.
Enabling Output
===============
There are two ways that users typically control log output. The
first is by setting the ``NS_LOG`` environment variable; e.g.:
.. sourcecode:: bash
$ NS_LOG="*" ./waf --run first
will run the ``first`` tutorial program with all logging output. (The
specifics of the ``NS_LOG`` format will be discussed below.)
This can be made more granular by selecting individual components:
.. sourcecode:: bash
$ NS_LOG="Ipv4L3Protocol" ./waf --run first
The output can be further tailored with prefix options.
The second way to enable logging is to use explicit statements in your
program, such as in the ``first`` tutorial program::
int
main (int argc, char *argv[])
{
LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO);
LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO);
...
(The meaning of ``LOG_LEVEL_INFO``, and other possible values,
will be discussed below.)
``NS_LOG`` Syntax
=================
The ``NS_LOG`` environment variable contains a list of log components
and options. Log components are separated by \`:' characters:
.. sourcecode:: bash
$ NS_LOG="<log-component>:<log-component>..."
Options for each log component are given as flags after
each log component:
.. sourcecode:: bash
$ NS_LOG="<log-component>=<option>|<option>...:<log-component>..."
Options control the severity and level for that component,
and whether optional information should be included, such as the
simulation time, simulation node, function name, and the symbolic severity.
Log Components
==============
Generally a log component refers to a single source code ``.cc`` file,
and encompasses the entire file.
Some helpers have special methods to enable the logging of all components
in a module, spanning different compilation units, but logically grouped
together, such as the |ns3| wifi code::
WifiHelper wifiHelper;
wifiHelper.EnableLogComponents ();
The ``NS_LOG`` log component wildcard \`*' will enable all components.
To see what log components are defined, any of these will work:
.. sourcecode:: bash
$ NS_LOG="print-list" ./waf --run ...
$ NS_LOG="foo" # a token not matching any log-component
The first form will print the name and enabled flags for all log components
which are linked in; try it with ``scratch-simulator``.
The second form prints all registered log components,
then exit with an error.
Severity and Level Options
==========================
Individual messages belong to a single "severity class," set by the macro
creating the message. In the example above,
``NS_LOG_LOGIC(..)`` creates the message in the ``LOG_LOGIC`` severity class.
The following severity classes are defined as ``enum`` constants:
================ =====================================
Severity Class Meaning
================ =====================================
``LOG_NONE`` The default, no logging
``LOG_ERROR`` Serious error messages only
``LOG_WARN`` Warning messages
``LOG_DEBUG`` For use in debugging
``LOG_INFO`` Informational
``LOG_FUNCTION`` Function tracing
``LOG_LOGIC`` Control flow tracing within functions
================ =====================================
Typically one wants to see messages at a given severity class *and higher*.
This is done by defining inclusive logging "levels":
====================== ===========================================
Level Meaning
====================== ===========================================
``LOG_LEVEL_ERROR`` Only ``LOG_ERROR`` severity class messages.
``LOG_LEVEL_WARN`` ``LOG_WARN`` and above.
``LOG_LEVEL_DEBUG`` ``LOG_DEBUG`` and above.
``LOG_LEVEL_INFO`` ``LOG_INFO`` and above.
``LOG_LEVEL_FUNCTION`` ``LOG_FUNCTION`` and above.
``LOG_LEVEL_LOGIC`` ``LOG_LOGIC`` and above.
``LOG_LEVEL_ALL`` All severity classes.
``LOG_ALL`` Synonym for ``LOG_LEVEL_ALL``
====================== ===========================================
The severity class and level options can be given in the ``NS_LOG``
environment variable by these tokens:
============ =================
Class Level
============ =================
``error`` ``level_error``
``warn`` ``level_warn``
``debug`` ``level_debug``
``info`` ``level_info``
``function`` ``level_function``
``logic`` ``level_logic``
.. | ``level_all``
| ``all``
| ``*``
============ =================
Using a severity class token enables log messages at that severity only.
For example, ``NS_LOG="*=warn"`` won't output messages with severity ``error``.
``NS_LOG="*=level_debug"`` will output messages at severity levels
``debug`` and above.
Severity classes and levels can be combined with the \`|' operator:
``NS_LOG="*=level_warn|logic"`` will output messages at severity levels
``error``, ``warn`` and ``logic``.
The ``NS_LOG`` severity level wildcard \`*' and ``all``
are synonyms for ``level_all``.
For log components merely mentioned in ``NS_LOG``
.. sourcecode:: bash
$ NS_LOG="<log-component>:..."
the default severity is ``LOG_LEVEL_ALL``.
Prefix Options
==============
A number of prefixes can help identify
where and when a message originated, and at what severity.
The available prefix options (as ``enum`` constants) are
====================== ===========================================
Prefix Symbol Meaning
====================== ===========================================
``LOG_PREFIX_FUNC`` Prefix the name of the calling function.
``LOG_PREFIX_TIME`` Prefix the simulation time.
``LOG_PREFIX_NODE`` Prefix the node id.
``LOG_PREFIX_LEVEL`` Prefix the severity level.
``LOG_PREFIX_ALL`` Enable all prefixes.
====================== ===========================================
The prefix options are described briefly below.
The options can be given in the ``NS_LOG``
environment variable by these tokens:
================ =========
Token Alternate
================ =========
``prefix_func`` ``func``
``prefix_time`` ``time``
``prefix_node`` ``node``
``prefix_level`` ``level``
``prefix_all`` | ``all``
| ``*``
================ =========
For log components merely mentioned in ``NS_LOG``
.. sourcecode:: bash
$ NS_LOG="<log-component>:..."
the default prefix options are ``LOG_PREFIX_ALL``.
Severity Prefix
###############
The severity class of a message can be included with the options
``prefix_level`` or ``level``. For example, this value of ``NS_LOG``
enables logging for all log components (\`*') and all severity
classes (``=all``), and prefixes the message with the severity
class (``|prefix_level``).
.. sourcecode:: bash
$ NS_LOG="*=all|prefix_level" ./waf --run scratch-simulator
Scratch Simulator
[ERROR] error message
[WARN] warn message
[DEBUG] debug message
[INFO] info message
[FUNCT] function message
[LOGIC] logic message
Time Prefix
###########
The simulation time can be included with the options
``prefix_time`` or ``time``. This prints the simulation time in seconds.
Node Prefix
###########
The simulation node id can be included with the options
``prefix_node`` or ``node``.
Function Prefix
###############
The name of the calling function can be included with the options
``prefix_func`` or ``func``.
``NS_LOG`` Wildcards
####################
The log component wildcard \`*' will enable all components. To
enable all components at a specific severity level
use ``*=<severity>``.
The severity level option wildcard \`*' is a synonym for ``all``.
This must occur before any \`|' characters separating options.
To enable all severity classes, use ``<log-component>=*``,
or ``<log-component>=*|<options>``.
The option wildcard \`*' or token ``all`` enables all prefix options,
but must occur *after* a \`|' character. To enable a specific
severity class or level, and all prefixes, use
``<log-component>=<severity>|*``.
The combined option wildcard ``**`` enables all severities and all prefixes;
for example, ``<log-component>=**``.
The uber-wildcard ``***`` enables all severities and all prefixes
for all log components. These are all equivalent:
.. sourcecode:: bash
$ NS_LOG="***" ... $ NS_LOG="*=all|*" ... $ NS_LOG="*=*|all" ...
$ NS_LOG="*=**" ... $ NS_LOG="*=level_all|*" ... $ NS_LOG="*=*|prefix_all" ...
$ NS_LOG="*=*|*" ...
Be advised: even the trivial ``scratch-simulator`` produces over
46K lines of output with ``NS_LOG="***"``!
How to add logging to your code
*******************************
Adding logging to your code is very simple:
1. Invoke the ``NS_LOG_COMPONENT_DEFINE (...);`` macro
inside of ``namespace ns3``.
Create a unique string identifier (usually based on the name of the file
and/or class defined within the file) and register it with a macro call
such as follows:
::
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("Ipv4L3Protocol");
...
This registers ``Ipv4L3Protocol`` as a log component.
(The macro was carefully written to permit inclusion either within or
outside of namespace ``ns3``, and usage will vary across the codebase, but
the original intent was to register this *outside* of namespace ``ns3``
at file global scope.)
2. Add logging statements (macro calls) to your functions and function bodies.
Logging Macros
==============
The logging macros and associated severity levels are
================ ==========================
Severity Class Macro
================ ==========================
``LOG_NONE`` (none needed)
``LOG_ERROR`` ``NS_LOG_ERROR (...);``
``LOG_WARN`` ``NS_LOG_WARN (...);``
``LOG_DEBUG`` ``NS_LOG_DEBUG (...);``
``LOG_INFO`` ``NS_LOG_INFO (...);``
``LOG_FUNCTION`` ``NS_LOG_FUNCTION (...);``
``LOG_LOGIC`` ``NS_LOG_LOGIC (...);``
================ ==========================
The macros function as output streamers, so anything you can send to
``std::cout``, joined by ``<<`` operators, is allowed::
void MyClass::Check (int value, char * item)
{
NS_LOG_FUNCTION (this << arg << item);
if (arg > 10)
{
NS_LOG_ERROR ("encountered bad value " << value <<
" while checking " << name << "!");
}
...
}
Note that ``NS_LOG_FUNCTION`` automatically inserts a \`\ :literal:`,\ `'
(comma-space) separator between each of its arguments.
This simplifies logging of function arguments;
just concatenate them with ``<<`` as in the example above.
Unconditional Logging
=====================
As a convenience, the ``NS_LOG_UNCOND (...);`` macro will always log its
arguments, even if the associated log-component is not enabled at any
severity. This macro does not use any of the prefix options. Note that
logging is only enabled in debug builds; this macro won't produce
output in optimized builds.
Guidelines
==========
* Start every class method with ``NS_LOG_FUNCTION (this << args...);``
This enables easy function call tracing.
* Except: don't log operators or explicit copy constructors,
since these will cause infinite recursion and stack overflow.
* For methods without arguments use the same form:
``NS_LOG_FUNCTION (this);``
* For static functions:
* With arguments use ``NS_LOG_FUNCTION (...);`` as normal.
* Without arguments use ``NS_LOG_FUNCTION_NOARGS ();``
* Use ``NS_LOG_ERROR`` for serious error conditions that probably
invalidate the simulation execution.
* Use ``NS_LOG_WARN`` for unusual conditions that may be correctable.
Please give some hints as to the nature of the problem and how
it might be corrected.
* ``NS_LOG_DEBUG`` is usually used in an *ad hoc* way to understand
the execution of a model.
* Use ``NS_LOG_INFO`` for additional information about the execution,
such as the size of a data structure when adding/removing from it.
* Use ``NS_LOG_LOGIC`` to trace important logic branches within a function.
* Test that your logging changes do not break the code.
Run some example programs with all log components turned on (e.g.
``NS_LOG="***"``).
-573
View File
@@ -1,573 +0,0 @@
.. include:: replace.txt
.. highlight:: cpp
Creating a new |ns3| model
--------------------------
This chapter walks through the design process of an |ns3| model. In many
research cases, users will not be satisfied to merely adapt existing models, but
may want to extend the core of the simulator in a novel way. We will use the
example of adding an ErrorModel to a simple |ns3| link as a motivating example
of how one might approach this problem and proceed through a design and
implementation.
.. note:: Documentation
Here we focus on the process of creating new models
and new modules, and some of the design choices involved.
For the sake of clarity, we defer discussion of the
*mechanics* of documenting models and source code to the
:doc:`Documentation <documentation>` chapter.
Design Approach
***************
Consider how you want it to work; what should it do. Think about these things:
* *functionality:* What functionality should it have? What attributes or
configuration is exposed to the user?
* *reusability:* How much should others be able to reuse my design? Can I
reuse code from |ns2| to get started? How does a user integrate the model
with the rest of another simulation?
* *dependencies:* How can I reduce the introduction of outside dependencies on
my new code as much as possible (to make it more modular)? For instance,
should I avoid any dependence on IPv4 if I want it to also be used by IPv6?
Should I avoid any dependency on IP at all?
Do not be hesitant to contact the `ns-3-users` or `ns-developers` list if you have
questions. In particular, it is important to think about the public API of your
new model and ask for feedback. It also helps to let others know of your work in
case you are interested in collaborators.
Example: `ErrorModel`
+++++++++++++++++++++
An error model exists in |ns2|. It allows packets to be passed to a stateful
object that determines, based on a random variable, whether the packet is
corrupted. The caller can then decide what to do with the packet (drop it,
etc.).
The main API of the error model is a function to pass a packet to, and the
return value of this function is a boolean that tells the caller whether any
corruption occurred. Note that depending on the error model, the packet data
buffer may or may not be corrupted. Let's call this function "IsCorrupt()".
So far, in our design, we have::
class ErrorModel
{
public:
/**
* \returns true if the Packet is to be considered as errored/corrupted
* \param pkt Packet to apply error model to
*/
bool IsCorrupt (Ptr<Packet> pkt);
};
Note that we do not pass a const pointer, thereby allowing the function to
modify the packet if IsCorrupt() returns true. Not all error models will
actually modify the packet; whether or not the packet data buffer is corrupted
should be documented.
We may also want specialized versions of this, such as in |ns2|, so although it
is not the only design choice for polymorphism, we assume that we will subclass
a base class ErrorModel for specialized classes, such as RateErrorModel,
ListErrorModel, etc, such as is done in |ns2|.
You may be thinking at this point, "Why not make IsCorrupt() a virtual method?".
That is one approach; the other is to make the public non-virtual function
indirect through a private virtual function (this in C++ is known as the non
virtual interface idiom and is adopted in the |ns3| ErrorModel class).
Next, should this device have any dependencies on IP or other protocols? We do
not want to create dependencies on Internet protocols (the error model should be
applicable to non-Internet protocols too), so we'll keep that in mind later.
Another consideration is how objects will include this error model. We envision
putting an explicit setter in certain NetDevice implementations, for example.::
/**
* Attach a receive ErrorModel to the PointToPointNetDevice.
*
* The PointToPointNetDevice may optionally include an ErrorModel in
* the packet receive chain.
*
* @see ErrorModel
* @param em Ptr to the ErrorModel.
*/
void PointToPointNetDevice::SetReceiveErrorModel(Ptr<ErrorModel> em);
Again, this is not the only choice we have (error models could be aggregated to
lots of other objects), but it satisfies our primary use case, which is to allow
a user to force errors on otherwise successful packet transmissions, at the
NetDevice level.
After some thinking and looking at existing |ns2| code, here is a sample API of
a base class and first subclass that could be posted for initial review::
class ErrorModel
{
public:
ErrorModel ();
virtual ~ErrorModel ();
bool IsCorrupt (Ptr<Packet> pkt);
void Reset (void);
void Enable (void);
void Disable (void);
bool IsEnabled (void) const;
private:
virtual bool DoCorrupt (Ptr<Packet> pkt) = 0;
virtual void DoReset (void) = 0;
};
enum ErrorUnit
{
EU_BIT,
EU_BYTE,
EU_PKT
};
// Determine which packets are errored corresponding to an underlying
// random variable distribution, an error rate, and unit for the rate.
class RateErrorModel : public ErrorModel
{
public:
RateErrorModel ();
virtual ~RateErrorModel ();
enum ErrorUnit GetUnit (void) const;
void SetUnit (enum ErrorUnit error_unit);
double GetRate (void) const;
void SetRate (double rate);
void SetRandomVariable (const RandomVariable &ranvar);
private:
virtual bool DoCorrupt (Ptr<Packet> pkt);
virtual void DoReset (void);
};
Scaffolding
***********
Let's say that you are ready to start implementing; you have a fairly clear
picture of what you want to build, and you may have solicited some initial
review or suggestions from the list. One way to approach the next step
(implementation) is to create scaffolding and fill in the details as the design
matures.
This section walks through many of the steps you should consider to define
scaffolding, or a non-functional skeleton of what your model will eventually
implement. It is usually good practice to not wait to get these details
integrated at the end, but instead to plumb a skeleton of your model into the
system early and then add functions later once the API and integration seems
about right.
Note that you will want to modify a few things in the below presentation for
your model since if you follow the error model verbatim, the code you produce
will collide with the existing error model. The below is just an outline of how
ErrorModel was built that you can adapt to other models.
Review the |ns3| Coding Style Document
++++++++++++++++++++++++++++++++++++++
At this point, you may want to pause and read the |ns3| coding style document,
especially if you are considering to contribute your code back to the project.
The coding style document is linked off the main project page: `ns-3 coding
style <http://www.nsnam.org/developers/contributing-code/coding-style/>`_.
Decide Where in the Source Tree the Model Should Reside
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
All of the |ns3| model source code is in the directory ``src/``. You will need
to choose which subdirectory it resides in. If it is new model code of some
sort, it makes sense to put it into the ``src/`` directory somewhere,
particularly for ease of integrating with the build system.
In the case of the error model, it is very related to the packet class, so it
makes sense to implement this in the ``src/network/`` module where |ns3|
packets are implemented.
`waf` and `wscript`
+++++++++++++++++++
|ns3| uses the `Waf <http://www.freehackers.org/~tnagy/waf.html>`_ build system.
You will want to integrate your new |ns3| uses the Waf build system. You will
want to integrate your new source files into this system. This requires that you
add your files to the ``wscript`` file found in each directory.
Let's start with empty files error-model.h and error-model.cc, and add this to
``src/network/wscript``. It is really just a matter of adding the .cc file to the
rest of the source files, and the .h file to the list of the header files.
Now, pop up to the top level directory and type "./test.py". You
shouldn't have broken anything by this operation.
Include Guards
++++++++++++++
Next, let's add some `include guards
<http://en.wikipedia.org/wiki/Include_guard>`_ in our header file.::
#ifndef ERROR_MODEL_H
#define ERROR_MODEL_H
...
#endif
`namespace ns3`
+++++++++++++++
|ns3| uses the |ns3| `namespace
<http://en.wikipedia.org/wiki/Namespace_(computer_science)#Use_in_common_languages>`_
to isolate its symbols from other namespaces. Typically, a user will next put
an |ns3| namespace block in both the cc and h file.::
namespace ns3 {
...
}
At this point, we have some skeletal files in which we can start defining
our new classes. The header file looks like this::
#ifndef ERROR_MODEL_H
#define ERROR_MODEL_H
namespace ns3 {
} // namespace ns3
#endif
while the ``error-model.cc`` file simply looks like this::
#include "error-model.h"
namespace ns3 {
} // namespace ns3
These files should compile since they don't really have any contents. We're now
ready to start adding classes.
Initial Implementation
**********************
At this point, we're still working on some scaffolding, but we can begin to
define our classes, with the functionality to be added later.
Inherit from the `Object` Class?
++++++++++++++++++++++++++++++++
This is an important design step; whether to use class :cpp:class:`Object` as a
base class for your new classes.
As described in the chapter on the |ns3| :ref:`Object-model`, classes that
inherit from class :cpp:class:`Object` get special properties:
* the |ns3| type and attribute system (see :ref:`Attributes`)
* an object aggregation system
* a smart-pointer reference counting system (class Ptr)
Classes that derive from class :cpp:class:`ObjectBase`} get the first two
properties above, but do not get smart pointers. Classes that derive from class
:cpp:class:`RefCountBase` get only the smart-pointer reference counting system.
In practice, class :cpp:class:`Object` is the variant of the three above that
the |ns3| developer will most commonly encounter.
In our case, we want to make use of the attribute system, and we will be passing
instances of this object across the |ns3| public API, so class
:cpp:class:`Object` is appropriate for us.
Initial Classes
+++++++++++++++
One way to proceed is to start by defining the bare minimum functions and see if
they will compile. Let's review what all is needed to implement when we derive
from class Object.::
#ifndef ERROR_MODEL_H
#define ERROR_MODEL_H
#include "ns3/object.h"
namespace ns3 {
class ErrorModel : public Object
{
public:
static TypeId GetTypeId (void);
ErrorModel ();
virtual ~ErrorModel ();
};
class RateErrorModel : public ErrorModel
{
public:
static TypeId GetTypeId (void);
RateErrorModel ();
virtual ~RateErrorModel ();
};
#endif
A few things to note here. We need to include ``object.h``. The convention in
|ns3| is that if the header file is co-located in the same directory, it may be
included without any path prefix. Therefore, if we were implementing ErrorModel
in ``src/core/model`` directory, we could have just said "``#include "object.h"``".
But we are in ``src/network/model``, so we must include it as "``#include
"ns3/object.h"``". Note also that this goes outside the namespace declaration.
Second, each class must implement a static public member function called
``GetTypeId (void)``.
Third, it is a good idea to implement constructors and destructors rather than
to let the compiler generate them, and to make the destructor virtual. In C++,
note also that copy assignment operator and copy constructors are auto-generated
if they are not defined, so if you do not want those, you should implement those
as private members. This aspect of C++ is discussed in Scott Meyers' Effective
C++ book. item 45.
Let's now look at some corresponding skeletal implementation code in the .cc
file.::
#include "error-model.h"
namespace ns3 {
NS_OBJECT_ENSURE_REGISTERED (ErrorModel);
TypeId ErrorModel::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::ErrorModel")
.SetParent<Object> ()
.SetGroupName ("Network")
;
return tid;
}
ErrorModel::ErrorModel ()
{
}
ErrorModel::~ErrorModel ()
{
}
NS_OBJECT_ENSURE_REGISTERED (RateErrorModel);
TypeId RateErrorModel::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::RateErrorModel")
.SetParent<ErrorModel> ()
.SetGroupName ("Network")
.AddConstructor<RateErrorModel> ()
;
return tid;
}
RateErrorModel::RateErrorModel ()
{
}
RateErrorModel::~RateErrorModel ()
{
}
What is the ``GetTypeId (void)`` function? This function does a few things. It
registers a unique string into the TypeId system. It establishes the hierarchy
of objects in the attribute system (via ``SetParent``). It also declares that
certain objects can be created via the object creation framework
(``AddConstructor``).
The macro ``NS_OBJECT_ENSURE_REGISTERED (classname)`` is needed also once for
every class that defines a new GetTypeId method, and it does the actual
registration of the class into the system. The :ref:`Object-model` chapter
discusses this in more detail.
Including External Files
++++++++++++++++++++++++
Logging Support
+++++++++++++++
*Here, write a bit about adding |ns3| logging macros. Note that
LOG_COMPONENT_DEFINE is done outside the namespace ns3*
Constructor, Empty Function Prototypes
++++++++++++++++++++++++++++++++++++++
Key Variables (Default Values, Attributes)
++++++++++++++++++++++++++++++++++++++++++
Test Program 1
++++++++++++++
Object Framework
++++++++++++++++
Adding a Sample Script
**********************
At this point, one may want to try to take the basic scaffolding defined above
and add it into the system. Performing this step now allows one to use a simpler
model when plumbing into the system and may also reveal whether any design or
API modifications need to be made. Once this is done, we will return to building
out the functionality of the ErrorModels themselves.
Add Basic Support in the Class
++++++++++++++++++++++++++++++
::
/* point-to-point-net-device.h */
class ErrorModel;
/**
* Error model for receive packet events
*/
Ptr<ErrorModel> m_receiveErrorModel;
Add Accessor
++++++++++++
::
void
PointToPointNetDevice::SetReceiveErrorModel (Ptr<ErrorModel> em)
{
NS_LOG_FUNCTION (this << em);
m_receiveErrorModel = em;
}
.AddAttribute ("ReceiveErrorModel",
"The receiver error model used to simulate packet loss",
PointerValue (),
MakePointerAccessor (&PointToPointNetDevice::m_receiveErrorModel),
MakePointerChecker<ErrorModel> ())
Plumb Into the System
+++++++++++++++++++++
::
void PointToPointNetDevice::Receive (Ptr<Packet> packet)
{
NS_LOG_FUNCTION (this << packet);
uint16_t protocol = 0;
if (m_receiveErrorModel && m_receiveErrorModel->IsCorrupt (packet) )
{
//
// If we have an error model and it indicates that it is time to lose a
// corrupted packet, don't forward this packet up, let it go.
//
m_dropTrace (packet);
}
else
{
//
// Hit the receive trace hook, strip off the point-to-point protocol header
// and forward this packet up the protocol stack.
//
m_rxTrace (packet);
ProcessHeader(packet, protocol);
m_rxCallback (this, packet, protocol, GetRemote ());
if (!m_promiscCallback.IsNull ())
{ m_promiscCallback (this, packet, protocol, GetRemote (),
GetAddress (), NetDevice::PACKET_HOST);
}
}
}
Create Null Functional Script
+++++++++++++++++++++++++++++
::
/* simple-error-model.cc */
// Error model
// We want to add an error model to node 3's NetDevice
// We can obtain a handle to the NetDevice via the channel and node
// pointers
Ptr<PointToPointNetDevice> nd3 = PointToPointTopology::GetNetDevice
(n3, channel2);
Ptr<ErrorModel> em = Create<ErrorModel> ();
nd3->SetReceiveErrorModel (em);
bool
ErrorModel::DoCorrupt (Packet& p)
{
NS_LOG_FUNCTION;
NS_LOG_UNCOND("Corrupt!");
return false;
}
At this point, we can run the program with our trivial ErrorModel plumbed into
the receive path of the PointToPointNetDevice. It prints out the string
"Corrupt!" for each packet received at node n3. Next, we return to the error
model to add in a subclass that performs more interesting error modeling.
Add a Subclass
**************
The trivial base class ErrorModel does not do anything interesting, but it
provides a useful base class interface (Corrupt () and Reset ()), forwarded to
virtual functions that can be subclassed. Let's next consider what we call a
BasicErrorModel which is based on the |ns2| ErrorModel class (in
``ns-2/queue/errmodel.{cc,h}``).
What properties do we want this to have, from a user interface perspective? We
would like for the user to be able to trivially swap out the type of ErrorModel
used in the NetDevice. We would also like the capability to set configurable
parameters.
Here are a few simple requirements we will consider:
* Ability to set the random variable that governs the losses (default is
UniformVariable)
* Ability to set the unit (bit, byte, packet, time) of granularity over which
errors are applied.
* Ability to set the rate of errors (e.g. 10^-3) corresponding to the above unit
of granularity.
* Ability to enable/disable (default is enabled)
How to Subclass
+++++++++++++++
We declare BasicErrorModel to be a subclass of ErrorModel as follows,::
class BasicErrorModel : public ErrorModel
{
public:
static TypeId GetTypeId (void);
...
private:
// Implement base class pure virtual functions
virtual bool DoCorrupt (Ptr<Packet> p);
virtual bool DoReset (void);
...
}
and configure the subclass GetTypeId function by setting a unique TypeId string
and setting the Parent to ErrorModel::
TypeId RateErrorModel::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::RateErrorModel")
.SetParent<ErrorModel> ()
.SetGroupName ("Network")
.AddConstructor<RateErrorModel> ()
...
Build Core Functions and Unit Tests
***********************************
Assert Macros
+++++++++++++
Writing Unit Tests
++++++++++++++++++
-384
View File
@@ -1,384 +0,0 @@
.. include:: replace.txt
.. highlight:: cpp
Adding a New Module to |ns3|
----------------------------
When you have created a group of related classes, examples, and tests,
they can be combined together into an |ns3| module so that they can be
used with existing |ns3| modules and by other researchers.
This chapter walks you through the steps necessary to add a new module
to |ns3|.
.. _Step-0:
Step 0 - Module Layout
**********************
All modules can be found in the ``src`` directory. Each module can be
found in a directory that has the same name as the module. For
example, the ``spectrum`` module can be found here: ``src/spectrum``.
We'll be quoting from the ``spectrum`` module for illustration.
A prototypical module has the following directory structure and
required files:
.. sourcecode:: text
src/
module-name/
bindings/
doc/
examples/
wscript
helper/
model/
test/
examples-to-run.py
wscript
Not all directories will be present in each module.
Step 1 - Create a Module Skeleton
*********************************
A python program is provided in the source directory that
will create a skeleton for a new module. For the purposes
of this discussion we will assume that your new module
is called ``new-module``. From the ``src`` directory, do the following
to create the new module:
.. sourcecode:: bash
$ ./create-module.py new-module
Next, ``cd`` into ``new-module``; you will find this directory layout:
.. sourcecode:: text
$ cd new-module
$ ls
doc examples helper model test wscript
In more detail, the ``create-module.py`` script will create the
directories as well as initial skeleton ``wscript``, ``.h``, ``.cc``
and ``.rst`` files. The complete module with skeleton files looks like this:
.. sourcecode:: text
src/
new-module/
doc/
new-module.rst
examples/
new-module-example.cc
wscript
helper/
new-module-helper.cc
new-module-helper.h
model/
new-module.cc
new-module.h
test/
new-module-test-suite.cc
wscript
(If required the ``bindings/`` directory listed in
:ref:`Step-0 <Step-0>` will be created automatically during
the build.)
We next walk through how to customize this module. Informing ``waf``
about the files which make up your module is done by editing the two
``wscript`` files. We will walk through the main steps in this chapter.
All |ns3| modules depend on the ``core`` module and usually on
other modules. This dependency is specified in the ``wscript`` file
(at the top level of the module, not the separate ``wscript`` file
in the ``examples`` directory!). In the skeleton ``wscript``
the call that will declare your new module to ``waf`` will look
like this (before editing):
.. sourcecode:: python
def build(bld):
module = bld.create_ns3_module('new-module', ['core'])
Let's assume that ``new-module`` depends on the ``internet``,
``mobility``, and ``aodv`` modules. After editing it the ``wscript`` file
should look like:
.. sourcecode:: python
def build(bld):
module = bld.create_ns3_module('new-module', ['internet', 'mobility', 'aodv'])
Note that only first level module dependencies should be listed, which
is why we removed ``core``; the ``internet`` module in turn depends on
``core``.
Your module will most likely have model source files. Initial skeletons
(which will compile successfully) are created in ``model/new-module.cc``
and ``model/new-module.h``.
If your module will have helper source files, then they will go into
the ``helper/`` directory; again, initial skeletons are created
in that directory.
Finally, it is good practice to write tests and examples. These will
almost certainly be required for new modules to be accepted into
the official |ns3| source tree. A skeleton
test suite and test case is created in the ``test/`` directory.
The skeleton test suite will contain the below constructor,
which declares a new unit test named ``new-module``,
with a single test case consisting of the class ``NewModuleTestCase1``::
NewModuleTestSuite::NewModuleTestSuite ()
: TestSuite ("new-module", UNIT)
{
AddTestCase (new NewModuleTestCase1);
}
Step 3 - Declare Source Files
*****************************
The public header and source code files for your new module
should be specified in the ``wscript`` file by modifying it with
your text editor.
As an example, after declaring the ``spectrum`` module,
the ``src/spectrum/wscript`` specifies the source code files
with the following list:
.. sourcecode:: python
def build(bld):
module = bld.create_ns3_module('spectrum', ['internet', 'propagation', 'antenna', 'applications'])
module.source = [
'model/spectrum-model.cc',
'model/spectrum-value.cc',
.
.
.
'model/microwave-oven-spectrum-value-helper.cc',
'helper/spectrum-helper.cc',
'helper/adhoc-aloha-noack-ideal-phy-helper.cc',
'helper/waveform-generator-helper.cc',
'helper/spectrum-analyzer-helper.cc',
]
The objects resulting from compiling these sources will be assembled
into a link library, which will be linked to any programs relying on this
module.
But how do such programs learn the public API of our new module? Read on!
Step 4 - Declare Public Header Files
************************************
The header files defining the public API of your model and helpers
also should be specified in the ``wscript`` file.
Continuing with the ``spectrum`` model illustration,
the public header files are specified with the following stanza.
(Note that the argument to the ``bld`` function tells
``waf`` to install this module's headers with the other |ns3| headers):
.. sourcecode:: python
headers = bld(features='ns3header')
headers.module = 'spectrum'
headers.source = [
'model/spectrum-model.h',
'model/spectrum-value.h',
.
.
.
'model/microwave-oven-spectrum-value-helper.h',
'helper/spectrum-helper.h',
'helper/adhoc-aloha-noack-ideal-phy-helper.h',
'helper/waveform-generator-helper.h',
'helper/spectrum-analyzer-helper.h',
]
Headers made public in this way will be accessible to users of your model
with include statements like
.. sourcecode:: cpp
#include "ns3/spectrum-model.h"
Headers used strictly internally in your implementation should not
be included here. They are still accessible to your implemenation by
include statements like
.. sourcecode:: cpp
#include "my-module-implementation.h"
Step 5 - Declare Tests
**********************
If your new module has tests, then they must be specified in your
``wscript`` file by modifying it with your text editor.
The ``spectrum`` model tests are specified with the following stanza:
.. sourcecode:: python
module_test = bld.create_ns3_module_test_library('spectrum')
module_test.source = [
'test/spectrum-interference-test.cc',
'test/spectrum-value-test.cc',
]
See :doc:`Tests <tests>` for more information on how to write test cases.
Step 6 - Declare Examples
*************************
If your new module has examples, then they must be specified in your
``examples/wscript`` file. (The skeleton top-level ``wscript`` will
recursively include ``examples/wscript`` only if the examples were
enabled at configure time.)
The ``spectrum`` model defines it's first example in
``src/spectrum/examples/wscript`` with
.. sourcecode:: python
def build(bld):
obj = bld.create_ns3_program('adhoc-aloha-ideal-phy',
['spectrum', 'mobility'])
obj.source = 'adhoc-aloha-ideal-phy.cc'
Note that the second argument to the function ``create_ns3_program()``
is the list of modules that the program being created depends on; again,
don't forget to include ``new-module`` in the list. It's best practice
to list only the direct module dependencies, and let ``waf`` deduce
the full dependency tree.
Occasionally, for clarity, you may want to split the implementation
for your example among several source files. In this case, just
include those files as additional explicit sources of the example:
.. sourcecode:: python
obj = bld.create_ns3_program('new-module-example', [new-module])
obj.source = ['new-module-example.cc', 'new-module-example-part.cc']
Python examples are specified using the following
function call. Note that the second argument for the function
``register_ns3_script()`` is the list of modules that the Python example
depends on:
.. sourcecode:: python
bld.register_ns3_script('new-module-example.py', ['new-module'])
Step 7 - Examples Run as Tests
******************************
In addition to running explicit test code, the test framework
can also be instrumented to run full example programs to
try to catch regressions in the examples. However, not all examples
are suitable for regression tests. The file ``test/examples-to-run.py``
controls the invocation of the examples when the test framework runs.
The ``spectrum`` model examples run by ``test.py`` are specified in
``src/spectrum/test/examples-to-run.py`` using the following
two lists of C++ and Python examples:
.. sourcecode:: python
# A list of C++ examples to run in order to ensure that they remain
# buildable and runnable over time. Each tuple in the list contains
#
# (example_name, do_run, do_valgrind_run).
#
# See test.py for more information.
cpp_examples = [
("adhoc-aloha-ideal-phy", "True", "True"),
("adhoc-aloha-ideal-phy-with-microwave-oven", "True", "True"),
("adhoc-aloha-ideal-phy-matrix-propagation-loss-model", "True", "True"),
]
# A list of Python examples to run in order to ensure that they remain
# runnable over time. Each tuple in the list contains
#
# (example_name, do_run).
#
# See test.py for more information.
python_examples = [
("sample-simulator.py", "True"),
]
As indicated in the comment, each entry in the C++ list of examples to run
contains the tuple ``(example_name, do_run, do_valgrind_run)``, where
* ``example_name`` is the executable to be run,
* ``do_run`` is a condition under which to run the example, and
* ``do_valgrind_run`` is a condition under which to run the example
under valgrind. (This is needed because NSC causes illegal instruction
crashes with some tests when they are run under valgrind.)
Note that the two conditions are Python statements that
can depend on ``waf`` configuration variables. For example,
.. sourcecode:: python
("tcp-nsc-lfn", "NSC_ENABLED == True", "NSC_ENABLED == False"),
Each entry in the Python list of examples to run contains the tuple
``(example_name, do_run)``, where, as for the C++ examples,
* ``example_name`` is the Python script to be run, and
* ``do_run`` is a condition under which to run the example.
Again, the condition is a Python statement that can
depend on ``waf`` configuration variables. For example,
.. sourcecode:: python
("realtime-udp-echo.py", "ENABLE_REAL_TIME == False"),
Step 8 - Configure and Build
****************************
You can now configure, build and test your module as normal.
You must reconfigure the project as a first step so that ``waf``
caches the new information in your ``wscript`` files,
or else your new module will not be included in the build.
.. sourcecode:: bash
$ ./waf configure --enable-examples --enable-tests
$ ./waf build
$ ./test.py
Look for your new module's test suite (and example programs,
if your module has any enabled) in the test output.
Step 9 - Python Bindings
************************
Adding Python bindings to your module is optional, and the step is
commented out by default in the ``create-module.py`` script.
.. sourcecode:: python
# bld.ns3_python_bindings()
If you want to include Python bindings (needed only if you want
to write Python ns-3 programs instead of C++ ns-3 programs), you
should uncomment the above and install the Python API scanning
system (covered elsewhere in this manual) and scan your module to
generate new bindings.
-298
View File
@@ -1,298 +0,0 @@
.. include:: replace.txt
.. highlight:: cpp
.. _Object-model:
Object model
------------
|ns3| is fundamentally a C++ object system. Objects can be declared and
instantiated as usual, per C++ rules. |ns3| also adds some features to
traditional C++ objects, as described below, to provide greater functionality
and features. This manual chapter is intended to introduce the reader to the
|ns3| object model.
This section describes the C++ class design for |ns3| objects. In brief,
several design patterns in use include classic object-oriented design
(polymorphic interfaces and implementations), separation of interface and
implementation, the non-virtual public interface design pattern, an object
aggregation facility, and reference counting for memory management. Those
familiar with component models such as COM or Bonobo will recognize elements of
the design in the |ns3| object aggregation model, although the |ns3| design is
not strictly in accordance with either.
Object-oriented behavior
************************
C++ objects, in general, provide common object-oriented capabilities
(abstraction, encapsulation, inheritance, and polymorphism) that are part
of classic object-oriented design. |ns3| objects make use of these
properties; for instance::
class Address
{
public:
Address ();
Address (uint8_t type, const uint8_t *buffer, uint8_t len);
Address (const Address & address);
Address &operator = (const Address &address);
...
private:
uint8_t m_type;
uint8_t m_len;
...
};
Object base classes
*******************
There are three special base classes used in |ns3|. Classes that inherit
from these base classes can instantiate objects with special properties.
These base classes are:
* class :cpp:class:`Object`
* class :cpp:class:`ObjectBase`
* class :cpp:class:`SimpleRefCount`
It is not required that |ns3| objects inherit from these class, but
those that do get special properties. Classes deriving from
class :cpp:class:`Object` get the following properties.
* the |ns3| type and attribute system (see :ref:`Attributes`)
* an object aggregation system
* a smart-pointer reference counting system (class Ptr)
Classes that derive from class :cpp:class:`ObjectBase` get the first two
properties above, but do not get smart pointers. Classes that derive from class
:cpp:class:`SimpleRefCount`: get only the smart-pointer reference counting
system.
In practice, class :cpp:class:`Object` is the variant of the three above that
the |ns3| developer will most commonly encounter.
.. _Memory-management-and-class-Ptr:
Memory management and class Ptr
*******************************
Memory management in a C++ program is a complex process, and is often done
incorrectly or inconsistently. We have settled on a reference counting design
described as follows.
All objects using reference counting maintain an internal reference count to
determine when an object can safely delete itself. Each time that a pointer is
obtained to an interface, the object's reference count is incremented by calling
``Ref()``. It is the obligation of the user of the pointer to explicitly
``Unref()`` the pointer when done. When the reference count falls to zero, the
object is deleted.
* When the client code obtains a pointer from the object itself through object
creation, or via GetObject, it does not have to increment the reference count.
* When client code obtains a pointer from another source (e.g., copying a
pointer) it must call ``Ref()`` to increment the reference count.
* All users of the object pointer must call ``Unref()`` to release the
reference.
The burden for calling :cpp:func:`Unref()` is somewhat relieved by the use of
the reference counting smart pointer class described below.
Users using a low-level API who wish to explicitly allocate
non-reference-counted objects on the heap, using operator new, are responsible
for deleting such objects.
Reference counting smart pointer (Ptr)
++++++++++++++++++++++++++++++++++++++
Calling ``Ref()`` and ``Unref()`` all the time would be cumbersome, so |ns3|
provides a smart pointer class :cpp:class:`Ptr` similar to
:cpp:class:`Boost::intrusive_ptr`. This smart-pointer class assumes that the
underlying type provides a pair of ``Ref`` and ``Unref`` methods that are
expected to increment and decrement the internal refcount of the object
instance.
This implementation allows you to manipulate the smart pointer as if it was a
normal pointer: you can compare it with zero, compare it against other pointers,
assign zero to it, etc.
It is possible to extract the raw pointer from this smart pointer with the
:cpp:func:`GetPointer` and :cpp:func:`PeekPointer` methods.
If you want to store a newed object into a smart pointer, we recommend you to
use the CreateObject template functions to create the object and store it in a
smart pointer to avoid memory leaks. These functions are really small
convenience functions and their goal is just to save you a small bit of typing.
CreateObject and Create
***********************
Objects in C++ may be statically, dynamically, or automatically created. This
holds true for |ns3| also, but some objects in the system have some additional
frameworks available. Specifically, reference counted objects are usually
allocated using a templated Create or CreateObject method, as follows.
For objects deriving from class :cpp:class:`Object`::
Ptr<WifiNetDevice> device = CreateObject<WifiNetDevice> ();
Please do not create such objects using ``operator new``; create them using
:cpp:func:`CreateObject()` instead.
For objects deriving from class :cpp:class:`SimpleRefCount`, or other objects
that support usage of the smart pointer class, a templated helper function is
available and recommended to be used::
Ptr<B> b = Create<B> ();
This is simply a wrapper around operator new that correctly handles the
reference counting system.
In summary, use ``Create<B>`` if B is not an object but just uses reference
counting (e.g. :cpp:class:`Packet`), and use ``CreateObject<B>`` if B derives
from :cpp:class:`ns3::Object`.
Aggregation
***********
The |ns3| object aggregation system is motivated in strong part by a recognition
that a common use case for |ns2| has been the use of inheritance and
polymorphism to extend protocol models. For instance, specialized versions of
TCP such as RenoTcpAgent derive from (and override functions from) class
TcpAgent.
However, two problems that have arisen in the |ns2| model are downcasts and
"weak base class." Downcasting refers to the procedure of using a base class
pointer to an object and querying it at run time to find out type information,
used to explicitly cast the pointer to a subclass pointer so that the subclass
API can be used. Weak base class refers to the problems that arise when a class
cannot be effectively reused (derived from) because it lacks necessary
functionality, leading the developer to have to modify the base class and
causing proliferation of base class API calls, some of which may not be
semantically correct for all subclasses.
|ns3| is using a version of the query interface design pattern to avoid these
problems. This design is based on elements of the `Component Object Model
<http://en.wikipedia.org/wiki/Component_Object_Model>`_ and `GNOME Bonobo
<http://en.wikipedia.org/wiki/Bonobo_(component_model)>`_ although full
binary-level compatibility of replaceable components is not supported and we
have tried to simplify the syntax and impact on model developers.
Examples
********
Aggregation example
+++++++++++++++++++
:cpp:class:`Node` is a good example of the use of aggregation in |ns3|. Note
that there are not derived classes of Nodes in |ns3| such as class
:cpp:class:`InternetNode`. Instead, components (protocols) are aggregated to a
node. Let's look at how some Ipv4 protocols are added to a node.::
static void
AddIpv4Stack(Ptr<Node> node)
{
Ptr<Ipv4L3Protocol> ipv4 = CreateObject<Ipv4L3Protocol> ();
ipv4->SetNode (node);
node->AggregateObject (ipv4);
Ptr<Ipv4Impl> ipv4Impl = CreateObject<Ipv4Impl> ();
ipv4Impl->SetIpv4 (ipv4);
node->AggregateObject (ipv4Impl);
}
Note that the Ipv4 protocols are created using :cpp:func:`CreateObject()`.
Then, they are aggregated to the node. In this manner, the Node base class does
not need to be edited to allow users with a base class Node pointer to access
the Ipv4 interface; users may ask the node for a pointer to its Ipv4 interface
at runtime. How the user asks the node is described in the next subsection.
Note that it is a programming error to aggregate more than one object of the
same type to an :cpp:class:`ns3::Object`. So, for instance, aggregation is not
an option for storing all of the active sockets of a node.
GetObject example
+++++++++++++++++
GetObject is a type-safe way to achieve a safe downcasting and to allow
interfaces to be found on an object.
Consider a node pointer ``m_node`` that points to a Node object that has an
implementation of IPv4 previously aggregated to it. The client code wishes to
configure a default route. To do so, it must access an object within the node
that has an interface to the IP forwarding configuration. It performs the
following::
Ptr<Ipv4> ipv4 = m_node->GetObject<Ipv4> ();
If the node in fact does not have an Ipv4 object aggregated to it, then the
method will return null. Therefore, it is good practice to check the return
value from such a function call. If successful, the user can now use the Ptr to
the Ipv4 object that was previously aggregated to the node.
Another example of how one might use aggregation is to add optional models to
objects. For instance, an existing Node object may have an "Energy Model" object
aggregated to it at run time (without modifying and recompiling the node class).
An existing model (such as a wireless net device) can then later "GetObject" for
the energy model and act appropriately if the interface has been either built in
to the underlying Node object or aggregated to it at run time. However, other
nodes need not know anything about energy models.
We hope that this mode of programming will require much less need for developers
to modify the base classes.
Object factories
****************
A common use case is to create lots of similarly configured objects. One can
repeatedly call :cpp:func:`CreateObject` but there is also a factory design
pattern in use in the |ns3| system. It is heavily used in the "helper" API.
Class :cpp:class:`ObjectFactory` can be used to instantiate objects and to
configure the attributes on those objects::
void SetTypeId (TypeId tid);
void Set (std::string name, const AttributeValue &value);
Ptr<T> Create (void) const;
The first method allows one to use the |ns3| TypeId system to specify the type
of objects created. The second allows one to set attributes on the objects to be
created, and the third allows one to create the objects themselves.
For example: ::
ObjectFactory factory;
// Make this factory create objects of type FriisPropagationLossModel
factory.SetTypeId ("ns3::FriisPropagationLossModel")
// Make this factory object change a default value of an attribute, for
// subsequently created objects
factory.Set ("SystemLoss", DoubleValue (2.0));
// Create one such object
Ptr<Object> object = factory.Create ();
factory.Set ("SystemLoss", DoubleValue (3.0));
// Create another object with a different SystemLoss
Ptr<Object> object = factory.Create ();
Downcasting
***********
A question that has arisen several times is, "If I have a base class pointer
(Ptr) to an object and I want the derived class pointer, should I downcast (via
C++ dynamic cast) to get the derived pointer, or should I use the object
aggregation system to :cpp:func:`GetObject\<> ()` to find a Ptr to the interface
to the subclass API?"
The answer to this is that in many situations, both techniques will work.
|ns3| provides a templated function for making the syntax of Object
dynamic casting much more user friendly::
template <typename T1, typename T2>
Ptr<T1>
DynamicCast (Ptr<T2> const&p)
{
return Ptr<T1> (dynamic_cast<T1 *> (PeekPointer (p)));
}
DynamicCast works when the programmer has a base type pointer and is testing
against a subclass pointer. GetObject works when looking for different objects
aggregated, but also works with subclasses, in the same way as DynamicCast. If
unsure, the programmer should use GetObject, as it works in all cases. If the
programmer knows the class hierarchy of the object under consideration, it is
more direct to just use DynamicCast.
-9
View File
@@ -1,9 +0,0 @@
.. include:: replace.txt
.. highlight:: cpp
.. _Object-names:
Object names
------------
*Placeholder chapter*
-61
View File
@@ -1,61 +0,0 @@
.. include:: replace.txt
.. highlight:: cpp
Organization
------------
This chapter describes the overall |ns3| software organization and the
corresponding organization of this manual.
|ns3| is a discrete-event network simulator in which the simulation core
and models are implemented in C++. |ns3| is built as a library which may be
statically or dynamically linked to a C++ main program that defines the
simulation topology and starts the simulator. |ns3| also exports nearly all
of its API to Python, allowing Python programs to import an "ns3" module in
much the same way as the |ns3| library is linked by executables in C++.
.. _software-organization:
.. figure:: figures/software-organization.*
Software organization of |ns3|
The source code for |ns3| is mostly organized in the ``src`` directory and
can be described by the diagram in :ref:`software-organization`. We will
work our way from the bottom up; in general, modules only have dependencies
on modules beneath them in the figure.
We first describe the core of the simulator; those components that are
common across all protocol, hardware, and environmental models.
The simulation core is implemented in ``src/core``. Packets are
fundamental objects in a network simulator
and are implemented in ``src/network``. These two simulation modules by
themselves are intended to comprise a generic simulation core that can be
used by different kinds of networks, not just Internet-based networks. The
above modules of |ns3| are independent of specific network and device
models, which are covered in subsequent parts of this manual.
In addition to the above |ns3| core, we introduce, also in the initial
portion of the manual, two other modules that supplement the core C++-based
API. |ns3| programs may access
all of the API directly or may make use of a so-called *helper API* that
provides convenient wrappers or encapsulation of low-level API calls. The
fact that |ns3| programs can be written to two APIs (or a combination
thereof) is a fundamental aspect of the simulator.
We also describe how Python is supported in |ns3| before moving onto
specific models of relevance to network simulation.
The remainder of the manual is focused on documenting the models and
supporting capabilities. The next part focuses on two fundamental objects in
|ns3|: the ``Node`` and ``NetDevice``. Two special NetDevice types are
designed to support network emulation use cases, and emulation is described
next. The following chapter is devoted to Internet-related models,
including the
sockets API used by Internet applications. The next chapter covers
applications, and the following chapter describes additional support for
simulation, such as animators and statistics.
The project maintains a separate manual devoted to testing and validation
of |ns3| code (see the `ns-3 Testing and Validation manual
<http://www.nsnam.org/tutorials.html>`_).
-315
View File
@@ -1,315 +0,0 @@
.. include:: replace.txt
.. highlight:: python
Using Python to Run |ns3|
-------------------------
Python bindings allow the C++ code in |ns3| to be called from Python.
This chapter shows you how to create a Python script that can run |ns3| and also the process of creating Python bindings for a C++ |ns3| module.
Introduction
************
The goal of Python bindings for |ns3| are two fold:
#. Allow the programmer to write complete simulation scripts in Python (http://www.python.org);
#. Prototype new models (e.g. routing protocols).
For the time being, the primary focus of the bindings is the first goal, but the second goal will eventually be supported as well.
Python bindings for |ns3| are being developed using a new tool called PyBindGen (http://code.google.com/p/pybindgen).
An Example Python Script that Runs |ns3|
****************************************
Here is some example code that is written in Python and that runs |ns3|, which is written in C++. This Python example can be found in ``examples/tutorial/first.py``:
::
import ns.applications
import ns.core
import ns.internet
import ns.network
import ns.point_to_point
ns.core.LogComponentEnable("UdpEchoClientApplication", ns.core.LOG_LEVEL_INFO)
ns.core.LogComponentEnable("UdpEchoServerApplication", ns.core.LOG_LEVEL_INFO)
nodes = ns.network.NodeContainer()
nodes.Create(2)
pointToPoint = ns.point_to_point.PointToPointHelper()
pointToPoint.SetDeviceAttribute("DataRate", ns.core.StringValue("5Mbps"))
pointToPoint.SetChannelAttribute("Delay", ns.core.StringValue("2ms"))
devices = pointToPoint.Install(nodes)
stack = ns.internet.InternetStackHelper()
stack.Install(nodes)
address = ns.internet.Ipv4AddressHelper()
address.SetBase(ns.network.Ipv4Address("10.1.1.0"), ns.network.Ipv4Mask("255.255.255.0"))
interfaces = address.Assign (devices);
echoServer = ns.applications.UdpEchoServerHelper(9)
serverApps = echoServer.Install(nodes.Get(1))
serverApps.Start(ns.core.Seconds(1.0))
serverApps.Stop(ns.core.Seconds(10.0))
echoClient = ns.applications.UdpEchoClientHelper(interfaces.GetAddress(1), 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))
clientApps = echoClient.Install(nodes.Get(0))
clientApps.Start(ns.core.Seconds(2.0))
clientApps.Stop(ns.core.Seconds(10.0))
ns.core.Simulator.Run()
ns.core.Simulator.Destroy()
Running Python Scripts
**********************
waf contains some options that automatically update the python path to find the ns3 module. To run example programs, there are two ways to use waf to take care of this. One is to run a waf shell; e.g.:
.. sourcecode:: bash
$ ./waf --shell
$ python examples/wireless/mixed-wireless.py
and the other is to use the --pyrun option to waf:
.. sourcecode:: bash
$ ./waf --pyrun examples/wireless/mixed-wireless.py
To run a python script under the C debugger:
.. sourcecode:: bash
$ ./waf --shell
$ gdb --args python examples/wireless/mixed-wireless.py
To run your own Python script that calls |ns3| and that has this path, ``/path/to/your/example/my-script.py``, do the following:
.. sourcecode:: bash
$ ./waf --shell
$ python /path/to/your/example/my-script.py
Caveats
*******
Python bindings for |ns3| are a work in progress, and some limitations are known by developers. Some of these limitations (not all) are listed here.
Incomplete Coverage
+++++++++++++++++++
First of all, keep in mind that not 100% of the API is supported in Python. Some of the reasons are:
#. some of the APIs involve pointers, which require knowledge of what kind of memory passing semantics (who owns what memory). Such knowledge is not part of the function signatures, and is either documented or sometimes not even documented. Annotations are needed to bind those functions;
#. Sometimes a unusual fundamental data type or C++ construct is used which is not yet supported by PyBindGen;
#. GCC-XML does not report template based classes unless they are instantiated.
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 we do not have manpower to complete 100% of the bindings.
Conversion Constructors
+++++++++++++++++++++++
`Conversion constructors <http://publib.boulder.ibm.com/infocenter/compbgpl/v9v111/topic/com.ibm.xlcpp9.bg.doc/language_ref/cplr384.htm>`_ are not fully supported yet by PyBindGen, and they always act as explicit constructors when translating an API into Python. For example, in C++ you can do this:
.. sourcecode:: cpp
Ipv4AddressHelper ipAddrs;
ipAddrs.SetBase ("192.168.0.0", "255.255.255.0");
ipAddrs.Assign (backboneDevices);
In Python, for the time being you have to do:
::
ipAddrs = ns3.Ipv4AddressHelper()
ipAddrs.SetBase(ns3.Ipv4Address("192.168.0.0"), ns3.Ipv4Mask("255.255.255.0"))
ipAddrs.Assign(backboneDevices)
CommandLine
+++++++++++
:cpp:func:`CommandLine::AddValue` works differently in Python than it does in |ns3|. In Python, the first parameter is a string that represents the command-line option name. When the option is set, an attribute with the same name as the option name is set on the :cpp:func:`CommandLine` object. Example:
::
NUM_NODES_SIDE_DEFAULT = 3
cmd = ns3.CommandLine()
cmd.NumNodesSide = None
cmd.AddValue("NumNodesSide", "Grid side number of nodes (total number of nodes will be this number squared)")
cmd.Parse(argv)
[...]
if cmd.NumNodesSide is None:
num_nodes_side = NUM_NODES_SIDE_DEFAULT
else:
num_nodes_side = int(cmd.NumNodesSide)
Tracing
+++++++
Callback based tracing is not yet properly supported for Python, as new |ns3| API needs to be provided for this to be supported.
Pcap file writing is supported via the normal API.
Ascii tracing is supported since |ns3|.4 via the normal C++ API translated to Python. However, ascii tracing requires the creation of an ostream object to pass into the ascii tracing methods. In Python, the C++ std::ofstream has been minimally wrapped to allow this. For example:
::
ascii = ns3.ofstream("wifi-ap.tr") # create the file
ns3.YansWifiPhyHelper.EnableAsciiAll(ascii)
ns3.Simulator.Run()
ns3.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.
Cygwin limitation
+++++++++++++++++
Python bindings do not work on Cygwin. This is due to a gccxml bug.
You might get away with it by re-scanning API definitions from within the
cygwin environment (./waf --python-scan). However the most likely solution
will probably have to be that we disable python bindings in CygWin.
If you really care about Python bindings on Windows, try building with mingw and native
python instead. Or else, to build without python bindings, disable python bindings in the configuration stage:
.. sourcecode:: bash
$ ./waf configure --disable-python
Working with Python Bindings
****************************
There are currently two kinds of Python bindings in |ns3|:
#. Monolithic bindings contain API definitions for all of the modules and can be found in a single directory, ``bindings/python``.
#. Modular bindings contain API definitions for a single module and can be found in each module's ``bindings`` directory.
Python Bindings Workflow
++++++++++++++++++++++++
The process by which Python bindings are handled is the following:
#. Periodically a developer uses a GCC-XML (http://www.gccxml.org) based API scanning script, which saves the scanned API definition as ``bindings/python/ns3_module_*.py`` files or as Python files in each modules' ``bindings`` directory. These files are kept under version control in the main |ns3| repository;
#. Other developers clone the repository and use the already scanned API definitions;
#. When configuring |ns3|, pybindgen will be automatically downloaded if not already installed. Released |ns3| tarballs will ship a copy of pybindgen.
If something goes wrong with compiling Python bindings and you just want to ignore them and move on with C++, you can disable Python with:
.. sourcecode:: bash
$ ./waf --disable-python
Instructions for Handling New Files or Changed API's
****************************************************
So you have been changing existing |ns3| APIs and Python bindings no longer compile? Do not despair, you can rescan the bindings to create new bindings that reflect the changes to the |ns3| API.
Depending on if you are using monolithic or modular bindings, see the discussions below to learn how to rescan your Python bindings.
Monolithic Python Bindings
**************************
Scanning the Monolithic Python Bindings
+++++++++++++++++++++++++++++++++++++++
To scan the monolithic Python bindings do the following:
.. sourcecode:: bash
$ ./waf --python-scan
Organization of the Monolithic Python Bindings
++++++++++++++++++++++++++++++++++++++++++++++
The monolithic Python API definitions are organized as follows. For each |ns3| module <name>, the file ``bindings/python/ns3_module_<name>.py`` describes its API. Each of those files have 3 toplevel functions:
#. :py:func:`def register_types(module)`: this function takes care of registering new types (e.g. C++ classes, enums) that are defined in tha module;
#. :py:func:`def register_methods(module)`: this function calls, for each class <name>, another function register_methods_Ns3<name>(module). These latter functions add method definitions for each class;
#. :py:func:`def register_functions(module)`: this function registers |ns3| functions that belong to that module.
Modular Python Bindings
***********************
Overview
++++++++
Since ns 3.11, the modular bindings are being added, in parallel to the old monolithic bindings.
The new python bindings are generated into an 'ns' namespace, instead of 'ns3' for the old bindings. Example:
::
from ns.network import Node
n1 = Node()
With modular Python bindings:
#. There is one separate Python extension module for each |ns3| module;
#. Scanning API definitions (apidefs) is done on a per ns- module basis;
#. Each module's apidefs files are stored in a 'bindings' subdirectory of the module directory;
Scanning the Modular Python Bindings
+++++++++++++++++++++++++++++++++++++++
To scan the modular Python bindings for the core module, for example, do the following:
.. sourcecode:: bash
$ ./waf --apiscan=core
To scan the modular Python bindings for all of the modules, do the following:
.. sourcecode:: bash
$ ./waf --apiscan=all
Creating a New Module
+++++++++++++++++++++
If you are adding a new module, Python bindings will continue to compile but will not cover the new module.
To cover a new module, you have to create a ``bindings/python/ns3_module_<name>.py`` file, similar to the what is described in the previous sections, and register it in the variable :cpp:func:`LOCAL_MODULES` in ``bindings/python/ns3modulegen.py``
Adding Modular Bindings To A Existing Module
++++++++++++++++++++++++++++++++++++++++++++
To add support for modular bindings to an existing |ns3| module, simply add the following line to its wscript build() function:
::
bld.ns3_python_bindings()
Organization of the Modular Python Bindings
+++++++++++++++++++++++++++++++++++++++++++
The ``src/<module>/bindings`` directory may contain the following files, some of them optional:
* ``callbacks_list.py``: this is a scanned file, DO NOT TOUCH. Contains a list of Callback<...> template instances found in the scanned headers;
* ``modulegen__gcc_LP64.py``: this is a scanned file, DO NOT TOUCH. Scanned API definitions for the GCC, LP64 architecture (64-bit)
* ``modulegen__gcc_ILP32.py``: this is a scanned file, DO NOT TOUCH. Scanned API definitions for the GCC, ILP32 architecture (32-bit)
* ``modulegen_customizations.py``: you may optionally add this file in order to customize the pybindgen code generation
* ``scan-header.h``: you may optionally add this file to customize what header file is scanned for the module. Basically this file is scanned instead of ns3/<module>-module.h. Typically, the first statement is #include "ns3/<module>-module.h", plus some other stuff to force template instantiations;
* ``module_helpers.cc``: you may add additional files, such as this, to be linked to python extension module, but they have to be registered in the wscript. Look at src/core/wscript for an example of how to do so;
* ``<module>.py``: if this file exists, it becomes the "frontend" python module for the ns3 module, and the extension module (.so file) becomes _<module>.so instead of <module>.so. The <module>.py file has to import all symbols from the module _<module> (this is more tricky than it sounds, see src/core/bindings/core.py for an example), and then can add some additional pure-python definitions.
More Information for Developers
*******************************
If you are a developer and need more information on |ns3|'s Python bindings, please see the `Python Bindings wiki page <http://www.nsnam.org/wiki/NS-3_Python_Bindings>`_.
-382
View File
@@ -1,382 +0,0 @@
.. include:: replace.txt
.. highlight:: cpp
Random Variables
----------------
|ns3| contains a built-in pseudo-random number generator (PRNG). It is important
for serious users of the simulator to understand the functionality,
configuration, and usage of this PRNG, and to decide whether it is sufficient
for his or her research use.
Quick Overview
**************
|ns3| random numbers are provided via instances of
:cpp:class:`ns3::RandomVariableStream`.
* by default, |ns3| simulations use a fixed seed; if there is any randomness in
the simulation, each run of the program will yield identical results unless
the seed and/or run number is changed.
* in *ns-3.3* and earlier, |ns3| simulations used a random seed by default; this
marks a change in policy starting with *ns-3.4*.
* in *ns-3.14* and earlier, |ns3| simulations used a different wrapper class
called :cpp:class:`ns3::RandomVariable`. As of *ns-3.15*, this class has been
replaced by :cpp:class:`ns3::RandomVariableStream`; the underlying pseudo-random
number generator has not changed.
* to obtain randomness across multiple simulation runs, you must either set the
seed differently or set the run number differently. To set a seed, call
:cpp:func:`ns3::RngSeedManager::SetSeed` at the beginning of the program; to set
a run number with the same seed, call :cpp:func:`ns3::RngSeedManager::SetRun` at
the beginning of the program; see :ref:`seeding-and-independent-replications`.
* each RandomVariableStream used in |ns3| has a virtual random number generator
associated with it; all random variables use either a fixed or random seed
based on the use of the global seed (previous bullet);
* if you intend to perform multiple runs of the same scenario, with different
random numbers, please be sure to read the section on how to perform
independent replications: :ref:`seeding-and-independent-replications`.
Read further for more explanation about the random number facility for |ns3|.
Background
**********
Simulations use a lot of random numbers; one study
found that most network simulations spend as much as 50%
of the CPU generating random numbers. Simulation users need
to be concerned with the quality of the (pseudo) random numbers and
the independence between different streams of random numbers.
Users need to be concerned with a few issues, such as:
* the seeding of the random number generator and whether a
simulation outcome is deterministic or not,
* how to acquire different streams of random numbers that are
independent from one another, and
* how long it takes for streams to cycle
We will introduce a few terms here: a RNG provides a long sequence
of (pseudo) random numbers.
The length of this sequence is called the *cycle length*
or *period*, after which the RNG will repeat itself.
This sequence can
be partitioned into disjoint *streams*. A stream of a
RNG is a contiguous subset or block of the RNG sequence.
For instance, if the
RNG period is of length N, and two streams are provided from this
RNG, then
the first stream might use the first N/2 values and the second
stream might produce the second N/2 values. An important property
here is that the two streams are uncorrelated. Likewise, each
stream can be partitioned disjointedly to a number of
uncorrelated *substreams*. The underlying RNG hopefully
produces a pseudo-random sequence of numbers with a very long
cycle length, and partitions this into streams and substreams in an
efficient manner.
|ns3| uses the same underlying random number generator as does |ns2|: the
MRG32k3a generator from Pierre L'Ecuyer. A detailed description can be found in
http://www.iro.umontreal.ca/~lecuyer/myftp/papers/streams00.pdf. The MRG32k3a
generator provides :math:`1.8x10^{19}` independent streams of random numbers,
each of which consists of :math:`2.3x10^{15}` substreams. Each substream has a
period (*i.e.*, the number of random numbers before overlap) of
:math:`7.6x10^{22}`. The period of the entire generator is :math:`3.1x10^{57}`.
Class :cpp:class:`ns3::RandomVariableStream` is the public interface to this
underlying random number generator. When users create new random variables
(such as :cpp:class:`ns3::UniformRandomVariable`,
:cpp:class:`ns3::ExponentialRandomVariable`, etc.), they create an object that uses
one of the distinct, independent streams of the random number generator.
Therefore, each object of type :cpp:class:`ns3::RandomVariableStream` has,
conceptually, its own "virtual" RNG. Furthermore, each
:cpp:class:`ns3::RandomVariableStream` can be configured to use one of the set of
substreams drawn from the main stream.
An alternate implementation would be to allow each RandomVariable to have its
own (differently seeded) RNG. However, we cannot guarantee as strongly that the
different sequences would be uncorrelated in such a case; hence, we prefer to
use a single RNG and streams and substreams from it.
.. _seeding-and-independent-replications:
Creating random variables
*************************
|ns3| supports a number of random variable objects from the base class
:cpp:class:`RandomVariableStream`. These objects derive from
:cpp:class:`ns3::Object` and are handled by smart pointers.
The correct way to create these objects is to use the templated
`CreateObject<>` method, such as:
::
Ptr<UniformRandomVariable> x = CreateObject<UniformRandomVariable> ();
then you can access values by calling methods on the object such as:
::
myRandomNo = x->GetInteger ();
If you try to instead do something like this:
::
myRandomNo = UniformRandomVariable().GetInteger ();
your program will encounter a segmentation fault, because the implementation
relies on some attribute construction that occurs only when `CreateObject`
is called.
Much of the rest of this chapter now discusses the properties of the
stream of pseudo-random numbers generated from such objects, and how to
control the seeding of such objects.
Seeding and independent replications
************************************
|ns3| simulations can be configured to produce deterministic or random results.
If the |ns3| simulation is configured to use a fixed, deterministic seed with
the same run number, it should give the same output each time it is run.
By default, |ns3| simulations use a fixed seed and run number. These values
are stored in two :cpp:class:`ns3::GlobalValue` instances: ``g_rngSeed`` and
``g_rngRun``.
A typical use case is to run a simulation as a sequence of independent trials,
so as to compute statistics on a large number of independent runs. The user can
either change the global seed and rerun the simulation, or can advance the
substream state of the RNG, which is referred to as incrementing the run number.
A class :cpp:class:`ns3::RngSeedManager` provides an API to control the seeding and
run number behavior. This seeding and substream state setting must be called
before any random variables are created; e.g::
RngSeedManager::SetSeed (3); // Changes seed from default of 1 to 3
RngSeedManager::SetRun (7); // Changes run number from default of 1 to 7
// Now, create random variables
Ptr<UniformRandomVariable> x = CreateObject<UniformRandomVariable> ();
Ptr<ExponentialRandomVariable> y = CreateObject<ExponentialRandomVarlable> ();
...
Which is better, setting a new seed or advancing the substream state? There is
no guarantee that the streams produced by two random seeds will not overlap.
The only way to guarantee that two streams do not overlap is to use the
substream capability provided by the RNG implementation. *Therefore, use the
substream capability to produce multiple independent runs of the same
simulation.* In other words, the more statistically rigorous way to configure
multiple independent replications is to use a fixed seed and to advance the run
number. This implementation allows for a maximum of :math:`2.3x10^{15}`
independent replications using the substreams.
For ease of use, it is not necessary to control the seed and run number from
within the program; the user can set the ``NS_GLOBAL_VALUE`` environment
variable as follows:
.. sourcecode:: bash
$ NS_GLOBAL_VALUE="RngRun=3" ./waf --run program-name
Another way to control this is by passing a command-line argument; since this is
an |ns3| GlobalValue instance, it is equivalently done such as follows:
.. sourcecode:: bash
$ ./waf --command-template="%s --RngRun=3" --run program-name
or, if you are running programs directly outside of waf:
.. sourcecode:: bash
$ ./build/optimized/scratch/program-name --RngRun=3
The above command-line variants make it easy to run lots of different
runs from a shell script by just passing a different RngRun index.
Class RandomVariableStream
**************************
All random variables should derive from class :cpp:class:`RandomVariable`. This
base class provides a few methods for globally configuring the behavior
of the random number generator. Derived classes provide API for drawing random
variates from the particular distribution being supported.
Each RandomVariableStream created in the simulation is given a generator that is a
new RNGStream from the underlying PRNG. Used in this manner, the L'Ecuyer
implementation allows for a maximum of :math:`1.8x10^19` random variables. Each
random variable in a single replication can produce up to :math:`7.6x10^22`
random numbers before overlapping.
Base class public API
*********************
Below are excerpted a few public methods of class :cpp:class:`RandomVariableStream`
that access the next value in the substream.
::
/**
* \brief Returns a random double from the underlying distribution
* \return A floating point random value
*/
double GetValue (void) const;
/**
* \brief Returns a random integer from the underlying distribution
* \return Integer cast of ::GetValue()
*/
uint32_t GetInteger (void) const;
We have already described the seeding configuration above. Different
RandomVariable subclasses may have additional API.
Types of RandomVariables
************************
The following types of random variables are provided, and are documented in the
|ns3| Doxygen or by reading ``src/core/model/random-variable-stream.h``. Users
can also create their own custom random variables by deriving from class
:cpp:class:`RandomVariableStream`.
* class :cpp:class:`UniformRandomVariable`
* class :cpp:class:`ConstantRandomVariable`
* class :cpp:class:`SequentialRandomVariable`
* class :cpp:class:`ExponentialRandomVariable`
* class :cpp:class:`ParetoRandomVariable`
* class :cpp:class:`WeibullRandomVariable`
* class :cpp:class:`NormalRandomVariable`
* class :cpp:class:`LogNormalRandomVariable`
* class :cpp:class:`GammaRandomVariable`
* class :cpp:class:`ErlangRandomVariable`
* class :cpp:class:`TriangularRandomVariable`
* class :cpp:class:`ZipfRandomVariable`
* class :cpp:class:`ZetaRandomVariable`
* class :cpp:class:`DeterministicRandomVariable`
* class :cpp:class:`EmpiricalRandomVariable`
Semantics of RandomVariableStream objects
*****************************************
RandomVariableStream objects derive from :cpp:class:`ns3::Object` and are
handled by smart pointers.
RandomVariableStream instances can also be used in |ns3| attributes, which means
that values can be set for them through the |ns3| attribute system.
An example is in the propagation models for WifiNetDevice::
TypeId
RandomPropagationDelayModel::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::RandomPropagationDelayModel")
.SetParent<PropagationDelayModel> ()
.SetGroupName ("Propagation")
.AddConstructor<RandomPropagationDelayModel> ()
.AddAttribute ("Variable",
"The random variable which generates random delays (s).",
StringValue ("ns3::UniformRandomVariable"),
MakePointerAccessor (&RandomPropagationDelayModel::m_variable),
MakePointerChecker<RandomVariableStream> ())
;
return tid;
}
Here, the |ns3| user can change the default random variable for this
delay model (which is a UniformRandomVariable ranging from 0 to 1) through
the attribute system.
Using other PRNG
****************
There is presently no support for substituting a different underlying
random number generator (e.g., the GNU Scientific Library or the Akaroa
package). Patches are welcome.
Setting the stream number
*************************
The underlying MRG32k3a generator provides 2^64 independent streams.
In ns-3, these are assigned sequentially starting from the first stream as
new RandomVariableStream instances make their first call to GetValue().
As a result of how these RandomVariableStream objects are assigned to
underlying streams, the assignment is sensitive to perturbations of
the simulation configuration. The consequence is that if any aspect of the
simulation configuration is changed, the mapping of RandomVariables to
streams may (or may not) change.
As a concrete example, a user running a comparative study between routing
protocols may find that the act of changing one routing protocol for another
will notice that the underlying mobility pattern also changed.
Starting with ns-3.15, some control has been provided to users to allow
users to optionally fix the assignment of selected RandomVariableStream
objects to underlying streams. This is the ``Stream`` attribute, part
of the base class RandomVariableStream.
By partitioning the existing sequence of streams from before:
.. sourcecode:: text
<-------------------------------------------------------------------------->
stream 0 stream (2^64 - 1)
into two equal-sized sets:
.. sourcecode:: text
<-------------------------------------------------------------------------->
^ ^^ ^
| || |
stream 0 stream (2^63 - 1) stream 2^63 stream (2^64 - 1)
<- automatically assigned -----------><- assigned by user ----------------->
The first 2^63 streams continue to be automatically assigned, while
the last 2^63 are given stream indices starting with zero up to
2^63-1.
The assignment of streams to a fixed stream number is optional; instances
of RandomVariableStream that do not have a stream value assigned will
be assigned the next one from the pool of automatic streams.
To fix a RandomVariableStream to a particular underlying stream, assign
its ``Stream`` attribute to a non-negative integer (the default value
of -1 means that a value will be automatically allocated).
Publishing your results
***********************
When you publish simulation results, a key piece of configuration
information that you should always state is how you used the
the random number generator.
* what seeds you used,
* what RNG you used if not the default,
* how were independent runs performed,
* for large simulations, how did you check that you did not cycle.
It is incumbent on the researcher publishing results to include enough
information to allow others to reproduce his or her results. It is also
incumbent on the researcher to convince oneself that the random numbers used
were statistically valid, and to state in the paper why such confidence is
assumed.
Summary
*******
Let's review what things you should do when creating a simulation.
* Decide whether you are running with a fixed seed or random seed; a fixed seed
is the default,
* Decide how you are going to manage independent replications, if applicable,
* Convince yourself that you are not drawing more random values than the cycle
length, if you are running a very long simulation, and
* When you publish, follow the guidelines above about documenting your use of
the random number generator.
-96
View File
@@ -1,96 +0,0 @@
.. include:: replace.txt
.. highlight:: cpp
RealTime
--------
|ns3| has been designed for integration into testbed and virtual machine
environments. To integrate with real network stacks and emit/consume packets, a
real-time scheduler is needed to try to lock the simulation clock with the
hardware clock. We describe here a component of this: the RealTime scheduler.
The purpose of the realtime scheduler is to cause the progression of the
simulation clock to occur synchronously with respect to some external time base.
Without the presence of an external time base (wall clock), simulation time
jumps instantly from one simulated time to the next.
Behavior
********
When using a non-realtime scheduler (the default in |ns3|), the simulator
advances the simulation time to the next scheduled event. During event
execution, simulation time is frozen. With the realtime scheduler, the behavior
is similar from the perspective of simulation models (i.e., simulation time is
frozen during event execution), but between events, the simulator will attempt
to keep the simulation clock aligned with the machine clock.
When an event is finished executing, and the scheduler moves to the next event,
the scheduler compares the next event execution time with the machine clock. If
the next event is scheduled for a future time, the simulator sleeps until that
realtime is reached and then executes the next event.
It may happen that, due to the processing inherent in the execution of
simulation events, that the simulator cannot keep up with realtime. In such a
case, it is up to the user configuration what to do. There are two |ns3|
attributes that govern the behavior. The first is
``ns3::RealTimeSimulatorImpl::SynchronizationMode``. The two entries possible
for this attribute are ``BestEffort`` (the default) or ``HardLimit``. In
"BestEffort" mode, the simulator will just try to catch up to realtime by
executing events until it reaches a point where the next event is in the
(realtime) future, or else the simulation ends. In BestEffort mode, then, it is
possible for the simulation to consume more time than the wall clock time. The
other option "HardLimit" will cause the simulation to abort if the tolerance
threshold is exceeded. This attribute is
``ns3::RealTimeSimulatorImpl::HardLimit`` and the default is 0.1 seconds.
A different mode of operation is one in which simulated time is **not** frozen
during an event execution. This mode of realtime simulation was implemented but
removed from the |ns3| tree because of questions of whether it would be useful.
If users are interested in a realtime simulator for which simulation time does
not freeze during event execution (i.e., every call to ``Simulator::Now()``
returns the current wall clock time, not the time at which the event started
executing), please contact the ns-developers mailing list.
Usage
*****
The usage of the realtime simulator is straightforward, from a scripting
perspective. Users just need to set the attribute
``SimulatorImplementationType`` to the Realtime simulator, such as follows: ::
GlobalValue::Bind ("SimulatorImplementationType",
StringValue ("ns3::RealtimeSimulatorImpl"));
There is a script in ``examples/realtime/realtime-udp-echo.cc`` that
has an example of how to configure the realtime behavior. Try:
.. sourcecode:: bash
$ ./waf --run realtime-udp-echo
Whether the simulator will work in a best effort or hard limit policy fashion is
governed by the attributes explained in the previous section.
Implementation
**************
The implementation is contained in the following files:
* ``src/core/model/realtime-simulator-impl.{cc,h}``
* ``src/core/model/wall-clock-synchronizer.{cc,h}``
In order to create a realtime scheduler, to a first approximation you just want
to cause simulation time jumps to consume real time. We propose doing this using
a combination of sleep- and busy- waits. Sleep-waits cause the calling process
(thread) to yield the processor for some amount of time. Even though this
specified amount of time can be passed to nanosecond resolution, it is actually
converted to an OS-specific granularity. In Linux, the granularity is called a
Jiffy. Typically this resolution is insufficient for our needs (on the order of
a ten milliseconds), so we round down and sleep for some smaller number of
Jiffies. The process is then awakened after the specified number of Jiffies has
passed. At this time, we have some residual time to wait. This time is generally
smaller than the minimum sleep time, so we busy-wait for the remainder of the
time. This means that the thread just sits in a for loop consuming cycles until
the desired time arrives. After the combination of sleep- and busy-waits, the
elapsed realtime (wall) clock should agree with the simulation time of the next
event and the simulation proceeds.
-5
View File
@@ -1,5 +0,0 @@
.. |ns3| replace:: *ns-3*
.. |ns2| replace:: *ns-2*
.. |check| replace:: :math:`\checkmark`
-11
View File
@@ -1,11 +0,0 @@
Support
-------
.. toctree::
new-models
new-modules
documentation
enable-modules
enable-tests
troubleshoot
-219
View File
@@ -1,219 +0,0 @@
.. include:: replace.txt
Background
----------
**This chapter may be skipped by readers familiar with the basics of
software testing.**
Writing defect-free software is a difficult proposition. There are many
dimensions to the problem and there is much confusion regarding what is
meant by different terms in different contexts. We have found it worthwhile
to spend a little time reviewing the subject and defining some terms.
Software testing may be loosely defined as the process of executing a program
with the intent of finding errors. When one enters a discussion regarding
software testing, it quickly becomes apparent that there are many distinct
mind-sets with which one can approach the subject.
For example, one could break the process into broad functional categories
like ''correctness testing,'' ''performance testing,'' ''robustness testing''
and ''security testing.'' Another way to look at the problem is by life-cycle:
''requirements testing,'' ''design testing,'' ''acceptance testing,'' and
''maintenance testing.'' Yet another view is by the scope of the tested system.
In this case one may speak of ''unit testing,'' ''component testing,''
''integration testing,'' and ''system testing.'' These terms are also not
standardized in any way, and so ''maintenance testing'' and ''regression
testing'' may be heard interchangeably. Additionally, these terms are often
misused.
There are also a number of different philosophical approaches to software
testing. For example, some organizations advocate writing test programs
before actually implementing the desired software, yielding ''test-driven
development.'' Some organizations advocate testing from a customer perspective
as soon as possible, following a parallel with the agile development process:
''test early and test often.'' This is sometimes called ''agile testing.'' It
seems that there is at least one approach to testing for every development
methodology.
The |ns3| project is not in the business of advocating for any one of
these processes, but the project as a whole has requirements that help inform
the test process.
Like all major software products, |ns3| has a number of qualities that
must be present for the product to succeed. From a testing perspective, some
of these qualities that must be addressed are that |ns3| must be
''correct,'' ''robust,'' ''performant'' and ''maintainable.'' Ideally there
should be metrics for each of these dimensions that are checked by the tests
to identify when the product fails to meet its expectations / requirements.
Correctness
***********
The essential purpose of testing is to determine that a piece of software
behaves ''correctly.'' For |ns3| this means that if we simulate
something, the simulation should faithfully represent some physical entity or
process to a specified accuracy and precision.
It turns out that there are two perspectives from which one can view
correctness. Verifying that a particular model is implemented according
to its specification is generically called *verification*. The process of
deciding that the model is correct for its intended use is generically called
*validation*.
Validation and Verification
***************************
A computer model is a mathematical or logical representation of something. It
can represent a vehicle, an elephant (see
`David Harel's talk about modeling an elephant at SIMUTools 2009 <http://simutools.org/2009/>`_, or a networking card. Models can also represent
processes such as global warming, freeway traffic flow or a specification of a
networking protocol. Models can be completely faithful representations of a
logical process specification, but they necessarily can never completely
simulate a physical object or process. In most cases, a number of
simplifications are made to the model to make simulation computationally
tractable.
Every model has a *target system* that it is attempting to simulate. The
first step in creating a simulation model is to identify this target system and
the level of detail and accuracy that the simulation is desired to reproduce.
In the case of a logical process, the target system may be identified as ''TCP
as defined by RFC 793.'' In this case, it will probably be desirable to create
a model that completely and faithfully reproduces RFC 793. In the case of a
physical process this will not be possible. If, for example, you would like to
simulate a wireless networking card, you may determine that you need, ''an
accurate MAC-level implementation of the 802.11 specification and [...] a
not-so-slow PHY-level model of the 802.11a specification.''
Once this is done, one can develop an abstract model of the target system. This
is typically an exercise in managing the tradeoffs between complexity, resource
requirements and accuracy. The process of developing an abstract model has been
called *model qualification* in the literature. In the case of a TCP
protocol, this process results in a design for a collection of objects,
interactions and behaviors that will fully implement RFC 793 in |ns3|.
In the case of the wireless card, this process results in a number of tradeoffs
to allow the physical layer to be simulated and the design of a network device
and channel for ns-3, along with the desired objects, interactions and behaviors.
This abstract model is then developed into an |ns3| model that
implements the abstract model as a computer program. The process of getting the
implementation to agree with the abstract model is called *model
verification* in the literature.
The process so far is open loop. What remains is to make a determination that a
given ns-3 model has some connection to some reality -- that a model is an
accurate representation of a real system, whether a logical process or a physical
entity.
If one is going to use a simulation model to try and predict how some real
system is going to behave, there must be some reason to believe your results --
i.e., can one trust that an inference made from the model translates into a
correct prediction for the real system. The process of getting the ns-3 model
behavior to agree with the desired target system behavior as defined by the model
qualification process is called *model validation* in the literature. In the
case of a TCP implementation, you may want to compare the behavior of your ns-3
TCP model to some reference implementation in order to validate your model. In
the case of a wireless physical layer simulation, you may want to compare the
behavior of your model to that of real hardware in a controlled setting,
The |ns3| testing environment provides tools to allow for both model
validation and testing, and encourages the publication of validation results.
Robustness
**********
Robustness is the quality of being able to withstand stresses, or changes in
environments, inputs or calculations, etc. A system or design is ''robust''
if it can deal with such changes with minimal loss of functionality.
This kind of testing is usually done with a particular focus. For example, the
system as a whole can be run on many different system configurations to
demonstrate that it can perform correctly in a large number of environments.
The system can be also be stressed by operating close to or beyond capacity by
generating or simulating resource exhaustion of various kinds. This genre of
testing is called ''stress testing.''
The system and its components may be exposed to so-called ''clean tests'' that
demonstrate a positive result -- that is that the system operates correctly in
response to a large variation of expected configurations.
The system and its components may also be exposed to ''dirty tests'' which
provide inputs outside the expected range. For example, if a module expects a
zero-terminated string representation of an integer, a dirty test might provide
an unterminated string of random characters to verify that the system does not
crash as a result of this unexpected input. Unfortunately, detecting such
''dirty'' input and taking preventive measures to ensure the system does not
fail catastrophically can require a huge amount of development overhead. In
order to reduce development time, a decision was taken early on in the project
to minimize the amount of parameter validation and error handling in the
|ns3| codebase. For this reason, we do not spend much time on dirty
testing -- it would just uncover the results of the design decision we know
we took.
We do want to demonstrate that |ns3| software does work across some set
of conditions. We borrow a couple of definitions to narrow this down a bit.
The *domain of applicability* is a set of prescribed conditions for which
the model has been tested, compared against reality to the extent possible, and
judged suitable for use. The *range of accuracy* is an agreement between
the computerized model and reality within a domain of applicability.
The |ns3| testing environment provides tools to allow for setting up
and running test environments over multiple systems (buildbot) and provides
classes to encourage clean tests to verify the operation of the system over the
expected ''domain of applicability'' and ''range of accuracy.''
Performant
**********
Okay, ''performant'' isn't a real English word. It is, however, a very concise
neologism that is quite often used to describe what we want |ns3| to
be: powerful and fast enough to get the job done.
This is really about the broad subject of software performance testing. One of
the key things that is done is to compare two systems to find which performs
better (cf benchmarks). This is used to demonstrate that, for example,
|ns3| can perform a basic kind of simulation at least as fast as a
competing tool, or can be used to identify parts of the system that perform
badly.
In the |ns3| test framework, we provide support for timing various kinds
of tests.
Maintainability
***************
A software product must be maintainable. This is, again, a very broad
statement, but a testing framework can help with the task. Once a model has
been developed, validated and verified, we can repeatedly execute the suite
of tests for the entire system to ensure that it remains valid and verified
over its lifetime.
When a feature stops functioning as intended after some kind of change to the
system is integrated, it is called generically a *regression*.
Originally the
term regression referred to a change that caused a previously fixed bug to
reappear, but the term has evolved to describe any kind of change that breaks
existing functionality. There are many kinds of regressions that may occur
in practice.
A *local regression* is one in which a change affects the changed component
directly. For example, if a component is modified to allocate and free memory
but stale pointers are used, the component itself fails.
A *remote regression* is one in which a change to one component breaks
functionality in another component. This reflects violation of an implied but
possibly unrecognized contract between components.
An *unmasked regression* is one that creates a situation where a previously
existing bug that had no affect is suddenly exposed in the system. This may
be as simple as exercising a code path for the first time.
A *performance regression* is one that causes the performance requirements
of the system to be violated. For example, doing some work in a low level
function that may be repeated large numbers of times may suddenly render the
system unusable from certain perspectives.
The |ns3| testing framework provides tools for automating the process
used to validate and verify the code in nightly test suites to help quickly
identify possible regressions.
-814
View File
@@ -1,814 +0,0 @@
.. include:: replace.txt
.. highlight:: bash
Testing framework
-----------------
|ns3| consists of a simulation core engine, a set of models, example programs,
and tests. Over time, new contributors contribute models, tests, and
examples. A Python test program ``test.py`` serves as the test
execution manager; ``test.py`` can run test code and examples to
look for regressions, can output the results into a number of forms, and
can manage code coverage analysis tools. On top of this, we layer
*buildslaves* that are automated build robots that perform
robustness testing by running the test framework on different systems
and with different configuration options.
Buildslaves
***********
At the highest level of |ns3| testing are the buildslaves (build robots).
If you are unfamiliar with
this system look at `<https://ns-buildmaster.ee.washington.edu:8010/>`_.
This is an open-source automated system that allows |ns3| to be rebuilt
and tested daily. By running the buildbots on a number
of different systems we can ensure that |ns3| builds and executes
properly on all of its supported systems.
Users (and developers) typically will not interact with the buildslave system other
than to read its messages regarding test results. If a failure is detected in
one of the automated build and test jobs, the buildbot will send an email to the
*ns-commits* mailing list. This email will look something like
.. sourcecode: text
[Ns-commits] Build failed in Jenkins: daily-ubuntu-without-valgrind » Ubuntu-64-15.04 #926
...
281 of 285 tests passed (281 passed, 3 skipped, 1 failed, 0 crashed, 0 valgrind errors)
List of SKIPped tests:
ns3-tcp-cwnd
ns3-tcp-interoperability
nsc-tcp-loss
List of FAILed tests:
random-variable-stream-generators
+ exit 1
Build step 'Execute shell' marked build as failure
In the full details URL shown in the email, one can find links to the detailed test output.
The buildslave system will do its job quietly if there are no errors, and the
system will undergo build and test cycles every day to verify that all is well.
Test.py
*******
The buildbots use a Python program, ``test.py``, that is responsible for
running all of the tests and collecting the resulting reports into a human-
readable form. This program is also available for use by users and developers
as well.
``test.py`` is very flexible in allowing the user to specify the number
and kind of tests to run; and also the amount and kind of output to generate.
Before running ``test.py``, make sure that ns3's examples and tests
have been built by doing the following
::
$ ./waf configure --enable-examples --enable-tests
$ ./waf build
By default, ``test.py`` will run all available tests and report status
back in a very concise form. Running the command
::
$ ./test.py
will result in a number of ``PASS``, ``FAIL``, ``CRASH`` or ``SKIP``
indications followed by the kind of test that was run and its display name.
.. sourcecode:: text
Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'build' finished successfully (0.939s)
FAIL: TestSuite propagation-loss-model
PASS: TestSuite object-name-service
PASS: TestSuite pcap-file-object
PASS: TestSuite ns3-tcp-cwnd
...
PASS: TestSuite ns3-tcp-interoperability
PASS: Example csma-broadcast
PASS: Example csma-multicast
This mode is intended to be used by users who are interested in determining if
their distribution is working correctly, and by developers who are interested
in determining if changes they have made have caused any regressions.
There are a number of options available to control the behavior of ``test.py``.
if you run ``test.py --help`` you should see a command summary like:
.. sourcecode:: text
Usage: test.py [options]
Options:
-h, --help show this help message and exit
-b BUILDPATH, --buildpath=BUILDPATH
specify the path where ns-3 was built (defaults to the
build directory for the current variant)
-c KIND, --constrain=KIND
constrain the test-runner by kind of test
-e EXAMPLE, --example=EXAMPLE
specify a single example to run (no relative path is
needed)
-d, --duration print the duration of each test suite and example
-e EXAMPLE, --example=EXAMPLE
specify a single example to run (no relative path is
needed)
-u, --update-data If examples use reference data files, get them to re-
generate them
-f FULLNESS, --fullness=FULLNESS
choose the duration of tests to run: QUICK, EXTENSIVE,
or TAKES_FOREVER, where EXTENSIVE includes QUICK and
TAKES_FOREVER includes QUICK and EXTENSIVE (only QUICK
tests are run by default)
-g, --grind run the test suites and examples using valgrind
-k, --kinds print the kinds of tests available
-l, --list print the list of known tests
-m, --multiple report multiple failures from test suites and test
cases
-n, --nowaf do not run waf before starting testing
-p PYEXAMPLE, --pyexample=PYEXAMPLE
specify a single python example to run (with relative
path)
-r, --retain retain all temporary files (which are normally
deleted)
-s TEST-SUITE, --suite=TEST-SUITE
specify a single test suite to run
-t TEXT-FILE, --text=TEXT-FILE
write detailed test results into TEXT-FILE.txt
-v, --verbose print progress and informational messages
-w HTML-FILE, --web=HTML-FILE, --html=HTML-FILE
write detailed test results into HTML-FILE.html
-x XML-FILE, --xml=XML-FILE
write detailed test results into XML-FILE.xml
If one specifies an optional output style, one can generate detailed descriptions
of the tests and status. Available styles are ``text`` and ``HTML``.
The buildbots will select the HTML option to generate HTML test reports for the
nightly builds using
::
$ ./test.py --html=nightly.html
In this case, an HTML file named ''nightly.html'' would be created with a pretty
summary of the testing done. A ''human readable'' format is available for users
interested in the details.
::
$ ./test.py --text=results.txt
In the example above, the test suite checking the |ns3| wireless
device propagation loss models failed. By default no further information is
provided.
To further explore the failure, ``test.py`` allows a single test suite
to be specified. Running the command
::
$ ./test.py --suite=propagation-loss-model
or equivalently
::
$ ./test.py -s propagation-loss-model
results in that single test suite being run.
.. sourcecode:: text
FAIL: TestSuite propagation-loss-model
To find detailed information regarding the failure, one must specify the kind
of output desired. For example, most people will probably be interested in
a text file::
$ ./test.py --suite=propagation-loss-model --text=results.txt
This will result in that single test suite being run with the test status written to
the file ''results.txt''.
You should find something similar to the following in that file
.. sourcecode:: text
FAIL: Test Suite ''propagation-loss-model'' (real 0.02 user 0.01 system 0.00)
PASS: Test Case "Check ... Friis ... model ..." (real 0.01 user 0.00 system 0.00)
FAIL: Test Case "Check ... Log Distance ... model" (real 0.01 user 0.01 system 0.00)
Details:
Message: Got unexpected SNR value
Condition: [long description of what actually failed]
Actual: 176.395
Limit: 176.407 +- 0.0005
File: ../src/test/ns3wifi/propagation-loss-models-test-suite.cc
Line: 360
Notice that the Test Suite is composed of two Test Cases. The first test case
checked the Friis propagation loss model and passed. The second test case
failed checking the Log Distance propagation model. In this case, an SNR of
176.395 was found, and the test expected a value of 176.407 correct to three
decimal places. The file which implemented the failing test is listed as well
as the line of code which triggered the failure.
If you desire, you could just as easily have written an HTML file using the
``--html`` option as described above.
Typically a user will run all tests at least once after downloading
|ns3| to ensure that his or her environment has been built correctly
and is generating correct results according to the test suites. Developers
will typically run the test suites before and after making a change to ensure
that they have not introduced a regression with their changes. In this case,
developers may not want to run all tests, but only a subset. For example,
the developer might only want to run the unit tests periodically while making
changes to a repository. In this case, ``test.py`` can be told to constrain
the types of tests being run to a particular class of tests. The following
command will result in only the unit tests being run::
$ ./test.py --constrain=unit
Similarly, the following command will result in only the example smoke tests
being run::
$ ./test.py --constrain=unit
To see a quick list of the legal kinds of constraints, you can ask for them
to be listed. The following command
::
$ ./test.py --kinds
will result in the following list being displayed:
.. sourcecode:: text
Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'build' finished successfully (0.939s)Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
bvt: Build Verification Tests (to see if build completed successfully)
core: Run all TestSuite-based tests (exclude examples)
example: Examples (to see if example programs run successfully)
performance: Performance Tests (check to see if the system is as fast as expected)
system: System Tests (spans modules to check integration of modules)
unit: Unit Tests (within modules to check basic functionality)
Any of these kinds of test can be provided as a constraint using the ``--constraint``
option.
To see a quick list of all of the test suites available, you can ask for them
to be listed. The following command,
::
$ ./test.py --list
will result in a list of the test suite being displayed, similar to
.. sourcecode:: text
Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'build' finished successfully (0.939s)
Test Type Test Name
--------- ---------
performance many-uniform-random-variables-one-get-value-call
performance one-uniform-random-variable-many-get-value-calls
performance type-id-perf
system buildings-pathloss-test
system buildings-shadowing-test
system devices-mesh-dot11s-regression
system devices-mesh-flame-regression
system epc-gtpu
...
unit wimax-phy-layer
unit wimax-service-flow
unit wimax-ss-mac-layer
unit wimax-tlv
example adhoc-aloha-ideal-phy
example adhoc-aloha-ideal-phy-matrix-propagation-loss-model
example adhoc-aloha-ideal-phy-with-microwave-oven
example aodv
...
Any of these listed suites can be selected to be run by itself using the
``--suite`` option as shown above. Examples are handled differently.
Similarly to test suites, one can run a single C++ example program
using the ``--example`` option. Note that the relative path for the
example does not need to be included and that the executables built
for C++ examples do not have extensions. Furthermore, the example
must be registered as an example to the test framework; it is not
sufficient to create an example and run it through test.py; it must
be added to the relevant ``examples-to-run.py`` file, explained below.
Entering
::
$ ./test.py --example=udp-echo
results in that single example being run.
.. sourcecode:: text
PASS: Example examples/udp/udp-echo
You can specify the directory where |ns3| was built using the
``--buildpath`` option as follows.
::
$ ./test.py --buildpath=/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build/debug --example=wifi-simple-adhoc
One can run a single Python example program using the ``--pyexample``
option. Note that the relative path for the example must be included
and that Python examples do need their extensions. Entering
::
$ ./test.py --pyexample=examples/tutorial/first.py
results in that single example being run.
.. sourcecode:: text
PASS: Example examples/tutorial/first.py
Because Python examples are not built, you do not need to specify the
directory where |ns3| was built to run them.
Normally when example programs are executed, they write a large amount of trace
file data. This is normally saved to the base directory of the distribution
(e.g., /home/user/ns-3-dev). When ``test.py`` runs an example, it really
is completely unconcerned with the trace files. It just wants to to determine
if the example can be built and run without error. Since this is the case, the
trace files are written into a ``/tmp/unchecked-traces`` directory. If you
run the above example, you should be able to find the associated
``udp-echo.tr`` and ``udp-echo-n-1.pcap`` files there.
The list of available examples is defined by the contents of the ''examples''
directory in the distribution. If you select an example for execution using
the ``--example`` option, ``test.py`` will not make any attempt to decide
if the example has been configured or not, it will just try to run it and
report the result of the attempt.
When ``test.py`` runs, by default it will first ensure that the system has
been completely built. This can be defeated by selecting the ``--nowaf``
option.
::
$ ./test.py --list --nowaf
will result in a list of the currently built test suites being displayed, similar to:
.. sourcecode:: text
propagation-loss-model
ns3-tcp-cwnd
ns3-tcp-interoperability
pcap-file
object-name-service
random-variable-stream-generators
Note the absence of the ``Waf`` build messages.
``test.py`` also supports running the test suites and examples under valgrind.
Valgrind is a flexible program for debugging and profiling Linux executables. By
default, valgrind runs a tool called memcheck, which performs a range of memory-
checking functions, including detecting accesses to uninitialised memory, misuse
of allocated memory (double frees, access after free, etc.) and detecting memory
leaks. This can be selected by using the ``--grind`` option.
::
$ ./test.py --grind
As it runs, ``test.py`` and the programs that it runs indirectly, generate large
numbers of temporary files. Usually, the content of these files is not interesting,
however in some cases it can be useful (for debugging purposes) to view these files.
``test.py`` provides a ``--retain`` option which will cause these temporary
files to be kept after the run is completed. The files are saved in a directory
named ``testpy-output`` under a subdirectory named according to the current Coordinated
Universal Time (also known as Greenwich Mean Time).
::
$ ./test.py --retain
Finally, ``test.py`` provides a ``--verbose`` option which will print
large amounts of information about its progress. It is not expected that this
will be terribly useful unless there is an error. In this case, you can get
access to the standard output and standard error reported by running test suites
and examples. Select verbose in the following way::
$ ./test.py --verbose
All of these options can be mixed and matched. For example, to run all of the
|ns3| core test suites under valgrind, in verbose mode, while generating an HTML
output file, one would do::
$ ./test.py --verbose --grind --constrain=core --html=results.html
TestTaxonomy
************
As mentioned above, tests are grouped into a number of broadly defined
classifications to allow users to selectively run tests to address the different
kinds of testing that need to be done.
* Build Verification Tests
* Unit Tests
* System Tests
* Examples
* Performance Tests
Moreover, each test is further classified according to the expected time needed to
run it. Tests are classified as:
* QUICK
* EXTENSIVE
* TAKES_FOREVER
Note that specifying EXTENSIVE fullness will also run tests in QUICK category.
Specifying TAKES_FOREVER will run tests in EXTENSIVE and QUICK categories.
By default, only QUICK tests are ran.
As a rule of thumb, tests that must be run to ensure |ns3| coherence should be
QUICK (i.e., take a few seconds). Tests that could be skipped, but are nice to do
can be EXTENSIVE; these are tests that typically need minutes. TAKES_FOREVER is
left for tests that take a really long time, in the order of several minutes.
The main classification goal is to be able to run the buildbots in a reasonable
time, and still be able to perform more extensive tests when needed.
BuildVerificationTests
++++++++++++++++++++++
These are relatively simple tests that are built along with the distribution
and are used to make sure that the build is pretty much working. Our
current unit tests live in the source files of the code they test and are
built into the |ns3| modules; and so fit the description of BVTs. BVTs live
in the same source code that is built into the |ns3| code. Our current tests
are examples of this kind of test.
Unit Tests
++++++++++
Unit tests are more involved tests that go into detail to make sure that a
piece of code works as advertised in isolation. There is really no reason
for this kind of test to be built into an |ns3| module. It turns out, for
example, that the unit tests for the object name service are about the same
size as the object name service code itself. Unit tests are tests that
check a single bit of functionality that are not built into the |ns3| code,
but live in the same directory as the code it tests. It is possible that
these tests check integration of multiple implementation files in a module
as well. The file src/core/test/names-test-suite.cc is an example of this kind
of test. The file src/network/test/pcap-file-test-suite.cc is another example
that uses a known good pcap file as a test vector file. This file is stored
locally in the src/network directory.
System Tests
++++++++++++
System tests are those that involve more than one module in the system. We
have lots of this kind of test running in our current regression framework,
but they are typically overloaded examples. We provide a new place
for this kind of test in the directory ``src/test``. The file
src/test/ns3tcp/ns3-interop-test-suite.cc is an example of this kind of
test. It uses NSC TCP to test the |ns3| TCP implementation. Often there
will be test vectors required for this kind of test, and they are stored in
the directory where the test lives. For example,
ns3tcp-interop-response-vectors.pcap is a file consisting of a number of TCP
headers that are used as the expected responses of the |ns3| TCP under test
to a stimulus generated by the NSC TCP which is used as a ''known good''
implementation.
Examples
++++++++
The examples are tested by the framework to make sure they built and will
run. Nothing is checked, and currently the pcap files are just written off
into /tmp to be discarded. If the examples run (don't crash) they pass this
smoke test.
Performance Tests
+++++++++++++++++
Performance tests are those which exercise a particular part of the system
and determine if the tests have executed to completion in a reasonable time.
Running Tests
*************
Tests are typically run using the high level ``test.py`` program. To get a list of the available command-line options, run ``test.py --help``
The test program ``test.py`` will run both tests and those examples that
have been added to the list to check. The difference between tests
and examples is as follows. Tests generally check that specific simulation
output or events conforms to expected behavior. In contrast, the output
of examples is not checked, and the test program merely checks the exit
status of the example program to make sure that it runs without error.
Briefly, to run all tests, first one must configure tests during configuration
stage, and also (optionally) examples if examples are to be checked:
::
$ ./waf --configure --enable-examples --enable-tests
Then, build |ns3|, and after it is built, just run ``test.py``. ``test.py -h``
will show a number of configuration options that modify the behavior
of test.py.
The program ``test.py`` invokes, for C++ tests and examples, a lower-level
C++ program called ``test-runner`` to actually run the tests. As discussed
below, this ``test-runner`` can be a helpful way to debug tests.
Debugging Tests
***************
The debugging of the test programs is best performed running the low-level
test-runner program. The test-runner is the bridge from generic Python
code to |ns3| code. It is written in C++ and uses the automatic test
discovery process in the |ns3| code to find and allow execution of all
of the various tests.
The main reason why ``test.py`` is not suitable for debugging is that it is
not allowed for logging to be turned on using the ``NS_LOG`` environmental
variable when test.py runs. This limitation does not apply to the test-runner
executable. Hence, if you want to see logging output from your tests, you
have to run them using the test-runner directly.
In order to execute the test-runner, you run it like any other |ns3| executable
-- using ``waf``. To get a list of available options, you can type::
$ ./waf --run "test-runner --help"
You should see something like the following
.. sourcecode:: text
Usage: /home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build/utils/ns3-dev-test-runner-debug [OPTIONS]
Options:
--help : print these options
--print-test-name-list : print the list of names of tests available
--list : an alias for --print-test-name-list
--print-test-types : print the type of tests along with their names
--print-test-type-list : print the list of types of tests available
--print-temp-dir : print name of temporary directory before running
the tests
--test-type=TYPE : process only tests of type TYPE
--test-name=NAME : process only test whose name matches NAME
--suite=NAME : an alias (here for compatibility reasons only)
for --test-name=NAME
--assert-on-failure : when a test fails, crash immediately (useful
when running under a debugger
--stop-on-failure : when a test fails, stop immediately
--fullness=FULLNESS : choose the duration of tests to run: QUICK,
EXTENSIVE, or TAKES_FOREVER, where EXTENSIVE
includes QUICK and TAKES_FOREVER includes
QUICK and EXTENSIVE (only QUICK tests are
run by default)
--verbose : print details of test execution
--xml : format test run output as xml
--tempdir=DIR : set temp dir for tests to store output files
--datadir=DIR : set data dir for tests to read reference files
--out=FILE : send test result to FILE instead of standard output
--append=FILE : append test result to FILE instead of standard output
There are a number of things available to you which will be familiar to you if
you have looked at ``test.py``. This should be expected since the test-
runner is just an interface between ``test.py`` and |ns3|. You
may notice that example-related commands are missing here. That is because
the examples are really not |ns3| tests. ``test.py`` runs them
as if they were to present a unified testing environment, but they are really
completely different and not to be found here.
The first new option that appears here, but not in test.py is the ``--assert-on-failure``
option. This option is useful when debugging a test case when running under a
debugger like ``gdb``. When selected, this option tells the underlying
test case to cause a segmentation violation if an error is detected. This has
the nice side-effect of causing program execution to stop (break into the
debugger) when an error is detected. If you are using gdb, you could use this
option something like,
::
$ ./waf shell
$ cd build/utils
$ gdb ns3-dev-test-runner-debug
$ run --suite=global-value --assert-on-failure
If an error is then found in the global-value test suite, a segfault would be
generated and the (source level) debugger would stop at the ``NS_TEST_ASSERT_MSG``
that detected the error.
To run one of the tests directly from the test-runner
using ``waf``, you will need to specify the test suite to run.
So you could use the shell and do::
$ ./waf --run "test-runner --suite=pcap-file"
|ns3| logging is available when you run it this way, such as:
$ NS_LOG="Packet" ./waf --run "test-runner --suite=pcap-file"
Test output
+++++++++++
Many test suites need to write temporary files (such as pcap files)
in the process of running the tests. The tests then need a temporary directory
to write to. The Python test utility (test.py) will provide a temporary file
automatically, but if run stand-alone this temporary directory must be provided.
It can be annoying to continually have to provide
a ``--tempdir``, so the test runner will figure one out for you if you don't
provide one. It first looks for environment variables named ``TMP`` and
``TEMP`` and uses those. If neither ``TMP`` nor ``TEMP`` are defined
it picks ``/tmp``. The code then tacks on an identifier indicating what
created the directory (ns-3) then the time (hh.mm.ss) followed by a large random
number. The test runner creates a directory of that name to be used as the
temporary directory. Temporary files then go into a directory that will be
named something like
::
/tmp/ns-3.10.25.37.61537845
The time is provided as a hint so that you can relatively easily reconstruct
what directory was used if you need to go back and look at the files that were
placed in that directory.
Another class of output is test output like pcap traces that are generated
to compare to reference output. The test program will typically delete
these after the test suites all run. To disable the deletion of test
output, run ``test.py`` with the "retain" option:
::
$ ./test.py -r
and test output can be found in the ``testpy-output/`` directory.
Reporting of test failures
++++++++++++++++++++++++++
When you run a test suite using the test-runner it will run the test
and report PASS or FAIL.
To run more quietly, you need to specify an output file to which the tests will write their status using the ``--out`` option.
Try,
::
$ ./waf --run "test-runner --suite=pcap-file --out=myfile.txt"
Debugging test suite failures
+++++++++++++++++++++++++++++
To debug test crashes, such as
.. sourcecode:: text
CRASH: TestSuite ns3-wifi-interference
You can access the underlying test-runner program via gdb as follows, and
then pass the "--basedir=`pwd`" argument to run (you can also pass other
arguments as needed, but basedir is the minimum needed)::
$ ./waf --command-template="gdb %s" --run "test-runner"
Waf: Entering directory `/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build'
Waf: Leaving directory `/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build'
'build' finished successfully (0.380s)
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
L cense GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu"...
(gdb) r --suite=
Starting program: <..>/build/utils/ns3-dev-test-runner-debug --suite=ns3-wifi-interference
[Thread debugging using libthread_db enabled]
assert failed. file=../src/core/model/type-id.cc, line=138, cond="uid <= m_information.size () && uid != 0"
...
Here is another example of how to use valgrind to debug a memory problem
such as::
VALGR: TestSuite devices-mesh-dot11s-regression
$ ./waf --command-template="valgrind %s --suite=devices-mesh-dot11s-regression" --run test-runner
Class TestRunner
****************
The executables that run dedicated test programs use a TestRunner class. This
class provides for automatic test registration and listing, as well as a way to
execute the individual tests. Individual test suites use C++ global
constructors
to add themselves to a collection of test suites managed by the test runner.
The test runner is used to list all of the available tests and to select a test
to be run. This is a quite simple class that provides three static methods to
provide or Adding and Getting test suites to a collection of tests. See the
doxygen for class ``ns3::TestRunner`` for details.
Test Suite
**********
All |ns3| tests are classified into Test Suites and Test Cases. A
test suite is a collection of test cases that completely exercise a given kind
of functionality. As described above, test suites can be classified as,
* Build Verification Tests
* Unit Tests
* System Tests
* Examples
* Performance Tests
This classification is exported from the TestSuite class. This class is quite
simple, existing only as a place to export this type and to accumulate test
cases. From a user perspective, in order to create a new TestSuite in the
system one only has to define a new class that inherits from class ``TestSuite``
and perform these two duties.
The following code will define a new class that can be run by ``test.py``
as a ''unit'' test with the display name, ``my-test-suite-name``.
.. sourcecode:: cpp
class MySuite : public TestSuite
{
public:
MyTestSuite ();
};
MyTestSuite::MyTestSuite ()
: TestSuite ("my-test-suite-name", UNIT)
{
AddTestCase (new MyTestCase, TestCase::QUICK);
}
static MyTestSuite myTestSuite;
The base class takes care of all of the registration and reporting required to
be a good citizen in the test framework.
Avoid putting initialization logic into the test suite or test case
constructors. This is
because an instance of the test suite is created at run time
(due to the static variable above) regardless of whether the test is being
run or not. Instead, the TestCase provides a virtual ``DoSetup`` method
that can be specialized to perform setup before ``DoRun`` is called.
Test Case
*********
Individual tests are created using a TestCase class. Common models for the use
of a test case include "one test case per feature", and "one test case per method."
Mixtures of these models may be used.
In order to create a new test case in the system, all one has to do is to inherit
from the ``TestCase`` base class, override the constructor to give the test
case a name and override the ``DoRun`` method to run the test. Optionally,
override also the ``DoSetup`` method.
.. sourcecode:: cpp
class MyTestCase : public TestCase
{
MyTestCase ();
virtual void DoSetup (void);
virtual void DoRun (void);
};
MyTestCase::MyTestCase ()
: TestCase ("Check some bit of functionality")
{
}
void
MyTestCase::DoRun (void)
{
NS_TEST_ASSERT_MSG_EQ (true, true, "Some failure message");
}
Utilities
*********
There are a number of utilities of various kinds that are also part of the
testing framework. Examples include a generalized pcap file useful for
storing test vectors; a generic container useful for transient storage of
test vectors during test execution; and tools for generating presentations
based on validation and verification testing results.
These utilities are not documented here, but for example, please see
how the TCP tests found in ``src/test/ns3tcp/`` use pcap files and reference
output.
-13
View File
@@ -1,13 +0,0 @@
.. include:: replace.txt
Overview
--------
This chapter is concerned with the testing and validation of |ns3| software.
This chapter provides
* background about terminology and software testing
* a description of the ns-3 testing framework
* a guide to model developers or new model contributors for how to write tests
-9
View File
@@ -1,9 +0,0 @@
Tests
-----
.. toctree::
test-overview
test-background
test-framework
how-to-write-tests
File diff suppressed because it is too large Load Diff
-87
View File
@@ -1,87 +0,0 @@
.. include:: replace.txt
.. highlight:: bash
Troubleshooting
---------------
This chapter posts some information about possibly common errors in building
or running |ns3| programs.
Please note that the wiki
(`<http://www.nsnam.org/wiki/Troubleshooting>`_) may have contributed
items.
Build errors
************
Run-time errors
***************
Sometimes, errors can occur with a program after a successful build. These are
run-time errors, and can commonly occur when memory is corrupted or pointer
values are unexpectedly null.
Here is an example of what might occur::
$ ./waf --run tcp-point-to-point
Entering directory '/home/tomh/ns-3-nsc/build'
Compilation finished successfully
Command ['/home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point'] exited with code -11
The error message says that the program terminated unsuccessfully, but it is
not clear from this information what might be wrong. To examine more
closely, try running it under the `gdb debugger
<http://sources.redhat.com/gdb/>`_:
.. sourcecode:: bash
$ ./waf --run tcp-point-to-point --command-template="gdb %s"
Entering directory '/home/tomh/ns-3-nsc/build'
Compilation finished successfully
GNU gdb Red Hat Linux (6.3.0.0-1.134.fc5rh)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db
library "/lib/libthread_db.so.1".
(gdb) run
Starting program: /home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point
Reading symbols from shared object read from target memory...done.
Loaded system supplied DSO at 0xf5c000
Program received signal SIGSEGV, Segmentation fault.
0x0804aa12 in main (argc=1, argv=0xbfdfefa4)
at ../examples/tcp-point-to-point.cc:136
136 Ptr<Socket> localSocket = socketFactory->CreateSocket ();
(gdb) p localSocket
$1 = {m_ptr = 0x3c5d65}
(gdb) p socketFactory
$2 = {m_ptr = 0x0}
(gdb) quit
The program is running. Exit anyway? (y or n) y
Note first the way the program was invoked-- pass the command to run as an
argument to the command template "gdb %s".
This tells us that there was an attempt to dereference a null pointer
socketFactory.
Let's look around line 136 of tcp-point-to-point, as gdb suggests:
.. sourcecode:: cpp
Ptr<SocketFactory> socketFactory = n2->GetObject<SocketFactory> (Tcp::iid);
Ptr<Socket> localSocket = socketFactory->CreateSocket ();
localSocket->Bind ();
The culprit here is that the return value of GetObject is not being checked and
may be null.
Sometimes you may need to use the `valgrind memory checker
<http://valgrind.org>`_ for more subtle errors. Again, you invoke the use of
valgrind similarly::
$ ./waf --run tcp-point-to-point --command-template="valgrind %s"
-571
View File
@@ -1,571 +0,0 @@
EPSTOPDF = epstopdf
DIA = dia
CONVERT = convert
SRC = ../../src
# Temporary source directory, for build
SOURCETEMP = source-temp
FIGURES = $(SOURCETEMP)/figures
# list all model library .rst files that need to be copied to $SOURCETEMP
SOURCES = \
source/conf.py \
source/_static \
source/index.rst \
source/replace.txt \
source/organization.rst \
source/internet-models.rst \
source/network.rst \
source/emulation-overview.rst \
$(SRC)/antenna/doc/source/antenna.rst \
$(SRC)/antenna/doc/source/antenna-design.rst \
$(SRC)/antenna/doc/source/antenna-user.rst \
$(SRC)/antenna/doc/source/antenna-testing.rst \
$(SRC)/aodv/doc/aodv.rst \
$(SRC)/applications/doc/applications.rst \
$(SRC)/bridge/doc/bridge.rst \
$(SRC)/brite/doc/brite.rst \
$(SRC)/buildings/doc/source/buildings.rst \
$(SRC)/buildings/doc/source/buildings-design.rst \
$(SRC)/buildings/doc/source/buildings-user.rst \
$(SRC)/buildings/doc/source/buildings-testing.rst \
$(SRC)/buildings/doc/source/buildings-references.rst \
$(SRC)/click/doc/click.rst \
$(SRC)/csma/doc/csma.rst \
$(SRC)/dsdv/doc/dsdv.rst \
$(SRC)/dsr/doc/dsr.rst \
$(SRC)/mpi/doc/distributed.rst \
$(SRC)/energy/doc/energy.rst \
$(SRC)/fd-net-device/doc/fd-net-device.rst \
$(SRC)/tap-bridge/doc/tap.rst \
$(SRC)/mesh/doc/source/mesh.rst \
$(SRC)/lte/doc/source/lte.rst \
$(SRC)/lte/doc/source/lte-user.rst \
$(SRC)/lte/doc/source/lte-design.rst \
$(SRC)/lte/doc/source/lte-testing.rst \
$(SRC)/lte/doc/source/lte-profiling.rst \
$(SRC)/lte/doc/source/lte-references.rst \
$(SRC)/propagation/doc/propagation.rst \
$(SRC)/network/doc/network-overview.rst \
$(SRC)/network/doc/packets.rst \
$(SRC)/network/doc/error-model.rst \
$(SRC)/network/doc/sockets-api.rst \
$(SRC)/network/doc/simple.rst \
$(SRC)/network/doc/queue.rst \
$(SRC)/internet/doc/internet-stack.rst \
$(SRC)/internet/doc/ipv4.rst \
$(SRC)/internet/doc/ipv6.rst \
$(SRC)/internet/doc/routing-overview.rst \
$(SRC)/internet/doc/tcp.rst \
$(SRC)/internet/doc/codel.rst \
$(SRC)/mobility/doc/mobility.rst \
$(SRC)/olsr/doc/olsr.rst \
$(SRC)/openflow/doc/openflow-switch.rst \
$(SRC)/point-to-point/doc/point-to-point.rst \
$(SRC)/wifi/doc/source/wifi.rst \
$(SRC)/wifi/doc/source/wifi-design.rst \
$(SRC)/wifi/doc/source/wifi-user.rst \
$(SRC)/wifi/doc/source/wifi-testing.rst \
$(SRC)/wifi/doc/source/wifi-references.rst \
$(SRC)/wimax/doc/wimax.rst \
$(SRC)/uan/doc/uan.rst \
$(SRC)/topology-read/doc/topology.rst \
$(SRC)/spectrum/doc/spectrum.rst \
$(SRC)/stats/doc/adaptor.rst \
$(SRC)/stats/doc/aggregator.rst \
$(SRC)/stats/doc/collector.rst \
$(SRC)/stats/doc/data-collection-helpers.rst \
$(SRC)/stats/doc/data-collection-overview.rst \
$(SRC)/stats/doc/data-collection.rst \
$(SRC)/stats/doc/probe.rst \
$(SRC)/stats/doc/scope-and-limitations.rst \
$(SRC)/netanim/doc/animation.rst \
$(SRC)/flow-monitor/doc/flow-monitor.rst \
$(SRC)/wave/doc/wave.rst \
$(SRC)/sixlowpan/doc/sixlowpan.rst \
$(SRC)/lr-wpan/doc/lr-wpan.rst \
# list all model library figure files that need to be copied to
# $SOURCETEMP/figures. For each figure to be included in all
# documentation formats (html, latex...) the following formats are supported:
# 1) a single .dia file (preferred option, because it can be edited)
# 2) a single .eps file
# 3) both a .pdf and .png file
SOURCEFIGS = \
figures/testbed.dia \
figures/emulated-channel.dia \
$(SRC)/antenna/doc/source/figures/antenna-coordinate-system.dia \
$(SRC)/network/doc/packet.dia \
$(SRC)/network/doc/node.dia \
$(SRC)/network/doc/buffer.dia \
$(SRC)/network/doc/sockets-overview.dia \
$(SRC)/internet/doc/internet-node-send.dia \
$(SRC)/internet/doc/internet-node-recv.dia \
$(SRC)/internet/doc/routing.dia \
$(SRC)/internet/doc/routing-specialization.dia \
$(SRC)/wifi/doc/source/figures/WifiArchitecture.dia \
$(SRC)/wifi/doc/source/figures/snir.dia \
$(SRC)/wimax/doc/WimaxArchitecture.dia \
$(SRC)/lte/doc/source/figures/epc-ctrl-arch.dia \
$(SRC)/lte/doc/source/figures/epc-data-flow-dl.dia \
$(SRC)/lte/doc/source/figures/epc-data-flow-ul.dia \
$(SRC)/lte/doc/source/figures/epc-profiling-scenario.dia \
$(SRC)/lte/doc/source/figures/epc-topology.dia \
$(SRC)/lte/doc/source/figures/epc-topology-x2-enhanced.dia \
$(SRC)/lte/doc/source/figures/eutran-profiling-scenario.dia \
$(SRC)/lte/doc/source/figures/ff-example.dia \
$(SRC)/lte/doc/source/figures/ff-mac-saps.dia \
$(SRC)/lte/doc/source/figures/lte-arch-enb-data.dia \
$(SRC)/lte/doc/source/figures/lte-arch-enb-ctrl.dia \
$(SRC)/lte/doc/source/figures/lte-arch-ue-data.dia \
$(SRC)/lte/doc/source/figures/lte-arch-ue-ctrl.dia \
$(SRC)/lte/doc/source/figures/lte-enb-phy.dia \
$(SRC)/lte/doc/source/figures/lte-ue-phy.dia \
$(SRC)/lte/doc/source/figures/lte-epc-x2-handover-seq-diagram.dia \
$(SRC)/lte/doc/source/figures/lte-epc-e2e-data-protocol-stack.dia \
$(SRC)/lte/doc/source/figures/lte-interference-test-scenario.dia \
$(SRC)/lte/doc/source/figures/lte-subframe-structure.dia \
$(SRC)/lte/doc/source/figures/lte-epc-x2-interface.dia \
$(SRC)/lte/doc/source/figures/lte-harq-architecture.dia \
$(SRC)/lte/doc/source/figures/lte-harq-processes-scheme.dia \
$(SRC)/lte/doc/source/figures/ue-meas-consumer.dia \
$(SRC)/lte/doc/source/figures/ue-meas-piecewise-motion.dia \
$(SRC)/lte/doc/source/figures/ue-meas-piecewise-a1.dia \
$(SRC)/lte/doc/source/figures/ue-meas-piecewise-a1-hys.dia \
$(SRC)/lte/doc/source/figures/lte-cell-selection-timeline.dia \
$(SRC)/lte/doc/source/figures/lte-cell-selection-scenario.dia \
$(SRC)/lte/doc/source/figures/lte-handover-target-scenario.dia \
$(SRC)/lte/doc/source/figures/lena-dual-stripe.eps \
$(SRC)/lte/doc/source/figures/lte-mcs-index.eps \
$(SRC)/lte/doc/source/figures/lenaThrTestCase1.eps \
$(SRC)/lte/doc/source/figures/lenaThrTestCase2.eps \
$(SRC)/lte/doc/source/figures/runningTime10s.eps \
$(SRC)/lte/doc/source/figures/epcRunningTime.eps \
$(SRC)/lte/doc/source/figures/propagationModel.eps \
$(SRC)/lte/doc/source/figures/simulationTime.eps \
$(SRC)/lte/doc/source/figures/epcSimulationTime.eps \
$(SRC)/lte/doc/source/figures/epcEutranRunningTime.eps \
$(SRC)/lte/doc/source/figures/profiling-memory.eps \
$(SRC)/lte/doc/source/figures/lte-rlc-implementation-model.dia \
$(SRC)/lte/doc/source/figures/lte-rlc-data-txon-dl.dia \
$(SRC)/lte/doc/source/figures/lte-rlc-data-retx-dl.dia \
$(SRC)/lte/doc/source/figures/lte-rlc-data-txon-ul.dia \
$(SRC)/lte/doc/source/figures/lte-rlc-data-retx-ul.dia \
$(SRC)/lte/doc/source/figures/lte-epc-x2-entity-saps.dia \
$(SRC)/lte/doc/source/figures/lte-strongest-cell-handover-algorithm.eps \
$(SRC)/lte/doc/source/figures/lte-phy-interference.pdf \
$(SRC)/lte/doc/source/figures/lte-phy-interference.png \
$(SRC)/lte/doc/source/figures/helpers.pdf \
$(SRC)/lte/doc/source/figures/helpers.png \
$(SRC)/lte/doc/source/figures/mac-random-access-contention.pdf \
$(SRC)/lte/doc/source/figures/mac-random-access-contention.png \
$(SRC)/lte/doc/source/figures/mac-random-access-noncontention.pdf \
$(SRC)/lte/doc/source/figures/mac-random-access-noncontention.png \
$(SRC)/lte/doc/source/figures/rrc-connection-establishment.pdf \
$(SRC)/lte/doc/source/figures/rrc-connection-establishment.png \
$(SRC)/lte/doc/source/figures/rrc-connection-reconfiguration.pdf \
$(SRC)/lte/doc/source/figures/rrc-connection-reconfiguration.png \
$(SRC)/lte/doc/source/figures/rrc-connection-reconfiguration-handover.pdf \
$(SRC)/lte/doc/source/figures/rrc-connection-reconfiguration-handover.png \
$(SRC)/lte/doc/source/figures/nas-attach.pdf \
$(SRC)/lte/doc/source/figures/nas-attach.png \
$(SRC)/lte/doc/source/figures/lte-enb-rrc-states.pdf \
$(SRC)/lte/doc/source/figures/lte-enb-rrc-states.png \
$(SRC)/lte/doc/source/figures/lte-ue-rrc-states.pdf \
$(SRC)/lte/doc/source/figures/lte-ue-rrc-states.png \
$(SRC)/lte/doc/source/figures/fading_pedestrian.pdf \
$(SRC)/lte/doc/source/figures/fading_pedestrian.png \
$(SRC)/lte/doc/source/figures/fading_vehicular.pdf \
$(SRC)/lte/doc/source/figures/fading_vehicular.png \
$(SRC)/lte/doc/source/figures/fading_urban_3kmph.pdf \
$(SRC)/lte/doc/source/figures/fading_urban_3kmph.png \
$(SRC)/lte/doc/source/figures/fr-enhanced-fractional-frequency-reuse-scheme.dia \
$(SRC)/lte/doc/source/figures/fr-full-frequency-reuse-scheme.dia \
$(SRC)/lte/doc/source/figures/fr-hard-frequency-reuse-scheme.dia \
$(SRC)/lte/doc/source/figures/fr-soft-fractional-frequency-reuse-scheme.dia \
$(SRC)/lte/doc/source/figures/fr-soft-frequency-reuse-scheme-v1.dia \
$(SRC)/lte/doc/source/figures/fr-soft-frequency-reuse-scheme-v2.dia \
$(SRC)/lte/doc/source/figures/fr-strict-frequency-reuse-scheme.dia \
$(SRC)/lte/doc/source/figures/ffr-distributed-scheme.dia \
$(SRC)/lte/doc/source/figures/lte-fr-soft-1-rem.png \
$(SRC)/lte/doc/source/figures/lte-fr-soft-1-rem.pdf \
$(SRC)/lte/doc/source/figures/lte-ffr-soft-2-spectrum-trace.png \
$(SRC)/lte/doc/source/figures/lte-ffr-soft-2-spectrum-trace.pdf \
$(SRC)/lte/doc/source/figures/lte-fr-hard-1-rem.png \
$(SRC)/lte/doc/source/figures/lte-fr-hard-1-rem.pdf \
$(SRC)/lte/doc/source/figures/lte-fr-hard-2-rem.png \
$(SRC)/lte/doc/source/figures/lte-fr-hard-2-rem.pdf \
$(SRC)/lte/doc/source/figures/lte-fr-hard-3-rem.png \
$(SRC)/lte/doc/source/figures/lte-fr-hard-3-rem.pdf \
$(SRC)/lte/doc/source/figures/MCS_1_4.pdf \
$(SRC)/lte/doc/source/figures/MCS_1_4.png \
$(SRC)/lte/doc/source/figures/MCS_5_8.pdf \
$(SRC)/lte/doc/source/figures/MCS_5_8.png \
$(SRC)/lte/doc/source/figures/MCS_9_12.pdf \
$(SRC)/lte/doc/source/figures/MCS_9_12.png \
$(SRC)/lte/doc/source/figures/MCS_13_16.pdf \
$(SRC)/lte/doc/source/figures/MCS_13_16.png \
$(SRC)/lte/doc/source/figures/MCS_17_20.pdf \
$(SRC)/lte/doc/source/figures/MCS_17_20.png \
$(SRC)/lte/doc/source/figures/MCS_21_24.pdf \
$(SRC)/lte/doc/source/figures/MCS_21_24.png \
$(SRC)/lte/doc/source/figures/MCS_25_28.pdf \
$(SRC)/lte/doc/source/figures/MCS_25_28.png \
$(SRC)/lte/doc/source/figures/MCS_29_29.pdf \
$(SRC)/lte/doc/source/figures/MCS_29_29.png \
$(SRC)/lte/doc/source/figures/MCS_2_test.png \
$(SRC)/lte/doc/source/figures/MCS_2_test.pdf \
$(SRC)/lte/doc/source/figures/MCS_12_test.png \
$(SRC)/lte/doc/source/figures/MCS_12_test.pdf \
$(SRC)/lte/doc/source/figures/MCS_16_test.png \
$(SRC)/lte/doc/source/figures/MCS_16_test.pdf \
$(SRC)/lte/doc/source/figures/miesm_scheme.pdf \
$(SRC)/lte/doc/source/figures/miesm_scheme.png \
$(SRC)/lte/doc/source/figures/lte-dl-power-control.dia \
$(SRC)/lte/doc/source/figures/lte-ffr-scheduling.dia \
$(SRC)/lte/doc/source/figures/lte-handover-campaign-rem.pdf \
$(SRC)/lte/doc/source/figures/lte-handover-campaign-rem.png \
$(SRC)/lte/doc/source/figures/lte-legacy-handover-algorithm.pdf \
$(SRC)/lte/doc/source/figures/lte-legacy-handover-algorithm.png \
$(SRC)/uan/doc/auvmobility-classes.dia \
$(SRC)/stats/doc/Stat-framework-arch.png \
$(SRC)/stats/doc/Wifi-default.png \
$(SRC)/stats/doc/dcf-overview.dia \
$(SRC)/stats/doc/dcf-overview-with-aggregation.dia \
$(SRC)/stats/doc/file-example.png \
$(SRC)/stats/doc/gnuplot-aggregator.png \
$(SRC)/stats/doc/gnuplot-example.png \
$(SRC)/stats/doc/gnuplot-helper-example.png \
$(SRC)/stats/doc/seventh-packet-byte-count.png \
$(SRC)/netanim/doc/figures/PacketStatistics.png \
$(SRC)/netanim/doc/figures/PacketStatistics.pdf \
$(SRC)/netanim/doc/figures/NetAnim_3_105.png \
$(SRC)/netanim/doc/figures/NetAnim_3_105.pdf \
$(SRC)/netanim/doc/figures/Trajectory.png \
$(SRC)/netanim/doc/figures/Trajectory.pdf \
$(SRC)/netanim/doc/figures/NodeCountersChart.png \
$(SRC)/netanim/doc/figures/NodeCountersChart.pdf \
$(SRC)/netanim/doc/figures/NodeCountersTable.png \
$(SRC)/netanim/doc/figures/NodeCountersTable.pdf \
$(SRC)/netanim/doc/figures/RoutingTables.png \
$(SRC)/netanim/doc/figures/RoutingTables.pdf \
$(SRC)/netanim/doc/figures/PacketTimeline.png \
$(SRC)/netanim/doc/figures/PacketTimeline.pdf \
$(SRC)/spectrum/doc/spectrum-channel-phy-interface.png \
$(SRC)/spectrum/doc/spectrum-channel-phy-interface.pdf \
$(SRC)/spectrum/doc/spectrum-analyzer-example.eps \
$(SRC)/spectrum/doc/spectrum-tv-rand-geo-points.eps \
$(SRC)/spectrum/doc/spectrum-tv-8vsb.png \
$(SRC)/spectrum/doc/spectrum-tv-cofdm.png \
$(SRC)/lr-wpan/doc/lr-wpan-arch.dia \
$(SRC)/lr-wpan/doc/lr-wpan-data-example.dia \
$(SRC)/lr-wpan/doc/lr-wpan-primitives.dia \
$(SRC)/lr-wpan/doc/802-15-4-ber.eps \
$(SRC)/lr-wpan/doc/802-15-4-per-sens.eps \
$(SRC)/lr-wpan/doc/802-15-4-psr-distance.eps
# specify figures from which .png and .pdf figures need to be
# generated (all dia and eps figures)
IMAGES_EPS = \
$(FIGURES)/testbed.eps \
$(FIGURES)/emulated-channel.eps \
$(FIGURES)/antenna-coordinate-system.eps \
$(FIGURES)/packet.eps \
$(FIGURES)/node.eps \
$(FIGURES)/buffer.eps \
$(FIGURES)/sockets-overview.eps \
$(FIGURES)/internet-node-send.eps \
$(FIGURES)/internet-node-recv.eps \
$(FIGURES)/routing.eps \
$(FIGURES)/routing-specialization.eps \
$(FIGURES)/WifiArchitecture.eps \
$(FIGURES)/snir.eps \
$(FIGURES)/WimaxArchitecture.eps \
$(FIGURES)/dcf-overview.eps \
$(FIGURES)/dcf-overview-with-aggregation.eps \
$(FIGURES)/epc-ctrl-arch.eps \
$(FIGURES)/epc-data-flow-dl.eps \
$(FIGURES)/epc-data-flow-ul.eps \
$(FIGURES)/epc-profiling-scenario.eps \
$(FIGURES)/epc-topology.eps \
$(FIGURES)/epc-topology-x2-enhanced.eps \
$(FIGURES)/eutran-profiling-scenario.eps \
$(FIGURES)/ff-example.eps \
$(FIGURES)/ff-mac-saps.eps \
$(FIGURES)/lte-dl-power-control.eps \
$(FIGURES)/lte-ffr-scheduling.eps \
$(FIGURES)/fr-enhanced-fractional-frequency-reuse-scheme.eps \
$(FIGURES)/fr-full-frequency-reuse-scheme.eps \
$(FIGURES)/fr-hard-frequency-reuse-scheme.eps \
$(FIGURES)/fr-soft-fractional-frequency-reuse-scheme.eps \
$(FIGURES)/fr-soft-frequency-reuse-scheme-v1.eps \
$(FIGURES)/fr-soft-frequency-reuse-scheme-v2.eps \
$(FIGURES)/fr-strict-frequency-reuse-scheme.eps \
$(FIGURES)/ffr-distributed-scheme.eps \
$(FIGURES)/lte-arch-enb-data.eps \
$(FIGURES)/lte-arch-enb-ctrl.eps \
$(FIGURES)/lte-arch-ue-data.eps \
$(FIGURES)/lte-arch-ue-ctrl.eps \
$(FIGURES)/lte-enb-phy.eps \
$(FIGURES)/lte-ue-phy.eps \
$(FIGURES)/lte-epc-e2e-data-protocol-stack.eps \
$(FIGURES)/lte-interference-test-scenario.eps \
$(FIGURES)/lte-subframe-structure.eps \
$(FIGURES)/lte-epc-x2-interface.eps \
$(FIGURES)/lte-harq-architecture.eps \
$(FIGURES)/lte-harq-processes-scheme.eps \
$(FIGURES)/ue-meas-consumer.eps \
$(FIGURES)/ue-meas-piecewise-motion.eps \
$(FIGURES)/ue-meas-piecewise-a1.eps \
$(FIGURES)/ue-meas-piecewise-a1-hys.eps \
$(FIGURES)/lte-cell-selection-timeline.eps \
$(FIGURES)/lte-cell-selection-scenario.eps \
$(FIGURES)/lte-handover-target-scenario.eps \
$(FIGURES)/lena-dual-stripe.eps \
$(FIGURES)/lte-mcs-index.eps \
$(FIGURES)/lenaThrTestCase1.eps \
$(FIGURES)/lenaThrTestCase2.eps \
$(FIGURES)/runningTime10s.eps \
$(FIGURES)/epcRunningTime.eps \
$(FIGURES)/propagationModel.eps \
$(FIGURES)/simulationTime.eps \
$(FIGURES)/epcSimulationTime.eps \
$(FIGURES)/epcEutranRunningTime.eps \
$(FIGURES)/profiling-memory.eps \
$(FIGURES)/lte-rlc-implementation-model.eps \
$(FIGURES)/lte-rlc-data-txon-dl.eps \
$(FIGURES)/lte-rlc-data-retx-dl.eps \
$(FIGURES)/lte-rlc-data-txon-ul.eps \
$(FIGURES)/lte-rlc-data-retx-ul.eps \
$(FIGURES)/lte-epc-x2-handover-seq-diagram.eps \
$(FIGURES)/lte-epc-x2-entity-saps.eps \
$(FIGURES)/lte-strongest-cell-handover-algorithm.eps \
$(FIGURES)/auvmobility-classes.eps \
$(FIGURES)/spectrum-analyzer-example.eps \
$(FIGURES)/spectrum-tv-rand-geo-points.eps \
$(FIGURES)/lr-wpan-primitives.eps \
$(FIGURES)/lr-wpan-data-example.eps \
$(FIGURES)/lr-wpan-arch.eps \
$(FIGURES)/802-15-4-ber.eps \
$(FIGURES)/802-15-4-per-sens.eps \
$(FIGURES)/802-15-4-psr-distance.eps
# rescale pdf figures as necessary
$(FIGURES)/testbed.pdf_width = 5in
$(FIGURES)/emulated-channel.pdf_width = 6in
$(FIGURES)/antenna-coordinate-system.pdf_width = 7cm
$(FIGURES)/node.pdf_width = 5in
$(FIGURES)/packet.pdf_width = 4in
$(FIGURES)/buffer.pdf_width = 15cm
$(FIGURES)/sockets-overview.pdf_width = 10cm
$(FIGURES)/internet-node-send.pdf_width = 5in
$(FIGURES)/internet-node-recv.pdf_width = 5in
$(FIGURES)/routing.pdf_width = 6in
$(FIGURES)/routing-specialization.pdf_width = 5in
$(FIGURES)/snir.pdf_width = 3in
$(FIGURES)/lte-interference-test-scenario.pdf_width = 3in
$(FIGURES)/epc-ctrl-arch.pdf_width = 8cm
$(FIGURES)/epc-topology.pdf_width = 4in
$(FIGURES)/epc-topology-x2-enhanced.pdf_width = 14cm
$(FIGURES)/lte-arch-data-rrc-pdcp-rlc.pdf_width = 3in
$(FIGURES)/lte-epc-e2e-data-protocol-stack.pdf_width = 15cm
$(FIGURES)/ff-mac-saps.pdf_width = 5in
$(FIGURES)/ff-example.pdf_width = 5in
$(FIGURES)/lte-dl-power-control.pdf_width = 8cm
$(FIGURES)/lte-ffr-scheduling.pdf_width = 8cm
$(FIGURES)/fr-enhanced-fractional-frequency-reuse-scheme.pdf_width = 8cm
$(FIGURES)/fr-full-frequency-reuse-scheme.pdf_width = 8cm
$(FIGURES)/fr-hard-frequency-reuse-scheme.pdf_width = 8cm
$(FIGURES)/fr-soft-fractional-frequency-reuse-scheme.pdf_width = 8cm
$(FIGURES)/fr-soft-frequency-reuse-scheme-v1.pdf_width = 8cm
$(FIGURES)/fr-soft-frequency-reuse-scheme-v2.pdf_width = 8cm
$(FIGURES)/fr-strict-frequency-reuse-scheme.pdf_width = 8cm
$(FIGURES)/ffr-distributed-scheme.pdf_width = 8cm
$(FIGURES)/lte-arch-enb-data.pdf_width = 6cm
$(FIGURES)/lte-arch-enb-ctrl.pdf_width = 10cm
$(FIGURES)/lte-arch-ue-data.pdf_width = 6cm
$(FIGURES)/lte-arch-ue-ctrl.pdf_width = 10cm
$(FIGURES)/lte-rlc-implementation-model.pdf_width = 20in
$(FIGURES)/lte-rlc-data-txon-dl.pdf_width = 10cm
$(FIGURES)/lte-rlc-data-txon-ul.pdf_width = 10cm
$(FIGURES)/lte-rlc-data-retx-dl.pdf_width = 10cm
$(FIGURES)/lte-rlc-data-retx-ul.pdf_width = 10cm
$(FIGURES)/lte-epc-x2-entity-saps.pdf_width = 12cm
$(FIGURES)/lte-phy-interference.pdf_width = 12cm
$(FIGURES)/lte-subframe-structure.pdf_width = 2in
$(FIGURES)/mac-random-access-contention.pdf_width = 10cm
$(FIGURES)/mac-random-access-noncontention.pdf_width = 15cm
$(FIGURES)/helpers.pdf_width = 8cm
$(FIGURES)/auvmobility-classes.pdf_width = 10cm
$(FIGURES)/spectrum-analyzer-example.pdf_width = 15cm
$(FIGURES)/spectrum-tv-rand-geo-points.pdf_width = 8cm
$(FIGURES)/lr-wpan-primitives.pdf_width = 3in
$(FIGURES)/lr-wpan-arch.pdf_width = 2in
IMAGES_PNG = ${IMAGES_EPS:.eps=.png}
IMAGES_PDF = ${IMAGES_EPS:.eps=.pdf}
IMAGES = $(IMAGES_EPS) $(IMAGES_PNG) $(IMAGES_PDF)
RESCALE = ../../utils/rescale-pdf.sh
%.eps : %.dia
@echo dia $(notdir $<)
@$(DIA) -t eps $< -e $@ >/dev/null
%.png : %.dia
@echo dia $(notdir $<)
@$(DIA) -t png $< -e $@ >/dev/null
%.png : %.eps
@echo convert $(notdir $<)
@$(CONVERT) $< $@ >/dev/null
%.pdf : %.eps
@echo epstopdf $(notdir $<)
@$(EPSTOPDF) $< -o=$@ >/dev/null
@if test x$($@_width) != x; then $(RESCALE) $($@_width) $@ ; fi
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SOURCETEMP)
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
.NOTPARALLEL:
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
copy-sources: $(SOURCES)
@mkdir -p $(SOURCETEMP)
@mkdir -p $(FIGURES)
@cp -r -p $(SOURCES) $(SOURCETEMP)
@cp -r -p $(SOURCEFIGS) $(FIGURES)
clean:
-rm -rf $(BUILDDIR)/*
-rm -rf $(SOURCETEMP)
frag: pickle
@if test ! -d $(BUILDDIR)/frag; then mkdir $(BUILDDIR)/frag; fi
pushd $(BUILDDIR)/frag && ../../pickle-to-xml.py ../pickle/index.fpickle > navigation.xml && popd
cp -r $(BUILDDIR)/pickle/_images $(BUILDDIR)/frag
html: copy-sources $(IMAGES)
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml: copy-sources $(IMAGES)
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml: copy-sources $(IMAGES)
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle: copy-sources $(IMAGES)
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json: copy-sources $(IMAGES)
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp: copy-sources $(IMAGES)
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp: copy-sources $(IMAGES)
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ns-3.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ns-3.qhc"
devhelp: copy-sources $(IMAGES)
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/ns-3"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ns-3"
@echo "# devhelp"
epub: copy-sources $(IMAGES)
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex: copy-sources $(IMAGES)
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf: copy-sources $(IMAGES)
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
make -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text: copy-sources $(IMAGES)
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man: copy-sources $(IMAGES)
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
changes: copy-sources $(IMAGES)
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck: copy-sources $(IMAGEs)
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest: copy-sources $(IMAGES)
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
Binary file not shown.
Binary file not shown.
-2
View File
@@ -1,2 +0,0 @@
This directory stores .rst files that are used to build the model library
documentation but that are not stored with a particular module.
View File
-216
View File
@@ -1,216 +0,0 @@
# -*- coding: utf-8 -*-
#
# ns-3 documentation build configuration file, created by
# sphinx-quickstart on Tue Dec 14 09:00:39 2010.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.pngmath']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'ns-3'
copyright = u'2011, ns-3 project'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = 'ns-3-dev'
# The full version, including alpha/beta/rc tags.
release = 'ns-3-dev'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = []
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'ns3_html_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['../..']
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
html_title = 'Model Library'
# A shorter title for the navigation bar. Default is the same as html_title.
html_short_title = 'Models'
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = '%b %d, %Y %H:%M'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'ns-3doc'
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'ns-3-model-library.tex', u'ns-3 Model Library',
u'ns-3 project', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
latex_logo = '../../ns3_html_theme/static/ns-3.png'
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'ns-3-model-library', u'ns-3 Model Library',
[u'ns-3 project'], 1)
]
-86
View File
@@ -1,86 +0,0 @@
.. include:: replace.txt
Emulation Overview
------------------
|ns3| has been designed for integration into testbed and virtual machine
environments. We have addressed this need by providing two kinds of net devices.
The first kind of device is a file descriptor net device (``FdNetDevice``),
which is a generic device type that can read and write from a file descriptor.
By associating this file descriptor with different things on the host
system, different capabilities can be provided. For instance, the
FdNetDevice can be associated with an underlying packet socket to provide
emulation capabilities. This allows |ns3| simulations
to send data on a "real" network. The second kind, called a ``Tap``
``NetDevice`` allows a "real" host to participate in an |ns3| simulation as if
it were one of the simulated nodes. An |ns3| simulation may be constructed with
any combination of simulated or emulated devices.
**Note:** Prior to ns-3.17, the emulation capability was provided by a
special device called an ``Emu`` NetDevice; the ``Emu`` NetDevice has
been replaced by the ``FdNetDevice``.
One of the use-cases we want to support is that of a testbed. A concrete example
of an environment of this kind is the ORBIT testbed. ORBIT is a laboratory
emulator/field trial network arranged as a two dimensional grid of 400 802.11
radio nodes. We integrate with ORBIT by using their "imaging" process to load
and run |ns3| simulations on the ORBIT array. We can use our
``EmuFdNetDevice``
to drive the hardware in the testbed and we can accumulate results either using
the |ns3| tracing and logging functions, or the native ORBIT data gathering
techniques. See `<http://www.orbit-lab.org/>`_ for details on the ORBIT
testbed.
A simulation of this kind is shown in the following figure:
.. _testbed:
.. figure:: figures/testbed.*
Example Implementation of Testbed Emulation.
You can see that there are separate hosts, each running a subset of a "global"
simulation. Instead of an |ns3| channel connecting the hosts, we use real
hardware provided by the testbed. This allows |ns3| applications and protocol
stacks attached to a simulation node to communicate over real hardware.
We expect the primary use for this configuration will be to generate repeatable
experimental results in a real-world network environment that includes all of
the |ns3| tracing, logging, visualization and statistics gathering tools.
In what can be viewed as essentially an inverse configuration, we allow "real"
machines running native applications and protocol stacks to integrate with an
|ns3| simulation. This allows for the simulation of large networks connected to
a real machine, and also enables virtualization. A simulation of this kind is
shown in the following figure:
.. _emulated-channel:
.. figure:: figures/emulated-channel.*
Implementation overview of emulated channel.
Here, you will see that there is a single host with a number of virtual machines
running on it. An |ns3| simulation is shown running in the virtual machine shown
in the center of the figure. This simulation has a number of nodes with
associated |ns3| applications and protocol stacks that are talking to an |ns3|
channel through native simulated |ns3| net devices.
There are also two virtual machines shown at the far left and far right of the
figure. These VMs are running native (Linux) applications and protocol stacks.
The VM is connected into the simulation by a Linux Tap net device. The user-mode
handler for the Tap device is instantiated in the simulation and attached to a
proxy node that represents the native VM in the simulation. These handlers allow
the Tap devices on the native VMs to behave as if they were |ns3| net devices in
the simulation VM. This, in turn, allows the native software and protocol suites
in the native VMs to believe that they are connected to the simulated |ns3|
channel.
We expect the typical use case for this environment will be to analyze the
behavior of native applications and protocol suites in the presence of large
simulated |ns3| networks.
.. toctree::
fd-net-device
tap
-52
View File
@@ -1,52 +0,0 @@
.. only:: html or latex
ns-3 Model Library
==================
This is the *ns-3 Model Library* documentation. Primary documentation for the ns-3 project is
available in five forms:
* `ns-3 Doxygen <http://www.nsnam.org/doxygen/index.html>`_: Documentation of the public APIs of the simulator
* Tutorial, Manual, and Model Library *(this document)* for the `latest release <http://www.nsnam.org/documentation/latest/>`_ and `development tree <http://www.nsnam.org/ns-3-dev/documentation/>`_
* `ns-3 wiki <http://www.nsnam.org/wiki>`_
This document is written in `reStructuredText <http://docutils.sourceforge.net/rst.html>`_ for `Sphinx <http://sphinx.pocoo.org/>`_ and is maintained in the
``doc/models`` directory of ns-3's source code.
.. toctree::
:maxdepth: 1
organization
animation
antenna
aodv
applications
bridge
brite
buildings
click
csma
data-collection
dsdv
dsr
emulation-overview
energy
flow-monitor
internet-models
lr-wpan
lte
mesh
distributed
mobility
network
olsr
openflow-switch
point-to-point
propagation
spectrum
sixlowpan
topology
uan
wave
wifi
wimax
-11
View File
@@ -1,11 +0,0 @@
Internet Models
---------------
.. toctree::
internet-stack
ipv4
ipv6
routing-overview
tcp
codel
-11
View File
@@ -1,11 +0,0 @@
Network Module
--------------
.. toctree::
packets
error-model
network-overview
sockets-api
simple
queue
-53
View File
@@ -1,53 +0,0 @@
.. include:: replace.txt
Organization
------------
This manual compiles documentation for |ns3| models and supporting
software that enable users to construct network simulations.
It is important to distinguish between **modules** and **models**:
* |ns3| software is organized into separate *modules* that are each
built as a separate software library. Individual ns-3 programs can link
the modules (libraries) they need to conduct their simulation.
* |ns3| *models* are abstract representations of real-world objects,
protocols, devices, etc.
An |ns3| module may consist of more than one model (for instance, the
:mod:`internet` module contains models for both TCP and UDP). In general,
ns-3 models do not span multiple software modules, however.
This manual provides documentation about the models of |ns3|. It
complements two other sources of documentation concerning models:
* the model APIs are documented, from a programming perspective, using
`Doxygen <http://www.doxygen.org>`_. Doxygen for ns-3 models is available
`on the project web server <http://www.nsnam.org/docs/doxygen/index.html>`_.
* the |ns3| core is documented in the developer's manual. |ns3| models make
use of the facilities of the core, such as attributes, default values,
random numbers, test frameworks, etc. Consult the
`main web site <http://www.nsnam.org>`_ to find copies of the manual.
Finally, additional documentation about various aspects of |ns3| may
exist on the `project wiki <http://www.nsnam.org/wiki>`_.
A sample outline of how to write model library documentation can be
found by executing the ``create-module.py`` program and looking at the
template created in the file ``new-module/doc/new-module.rst``.
.. sourcecode:: bash
$ cd src
$ ./create-module.py new-module
The remainder of this document is organized alphabetically by module name.
If you are new to |ns3|, you might first want to read below about the network
module, which contains some fundamental models for the simulator.
The packet model, models for different address formats, and abstract
base classes for objects such as nodes, net devices, channels, sockets, and
applications are discussed there.
-3
View File
@@ -1,3 +0,0 @@
.. |ns3| replace:: *ns-3*
.. |ns2| replace:: *ns-2*
-42
View File
@@ -1,42 +0,0 @@
/**
* @anchor modules_anchor
*
* @defgroup constants Constants
* @brief Constants you can change
*
* @defgroup utils Utils
* @brief The utils directory is for various programs and scripts related
* to code coverage, test suites, style checking, and benchmarking.
*/
/**
* @defgroup core Core
* \brief The "core" module contains:
* - a time management class to hold a time and convert between various
* time units: ns3::Time
* - a scheduler base class used to implement new simulation event
* schedulers:
* ns3::Scheduler and ns3::SchedulerFactory
* - a simulator class used to create, schedule and cancel events:
* ns3::Simulator
* - a Functor class: ns3::Callback
* - an os-independent interface to get access to the elapsed wall clock
* time: ns3::SystemWallClockMs
* - a class to register regression tests with the test manager: ns3::Test
* and ns3::TestManager
* - debugging facilities: \ref debugging
* - \ref randomvariable
* - a base class for objects which need to support per-instance
* "attributes" and trace sources: ns3::ObjectBase
* - a base class for objects which need to support reference counting
* and dynamic object aggregation: ns3::Object
* - a smart-pointer class ns3::Ptr designed to work together with
* ns3::Object
* - a configuration class used to set and control all attributes and
* trace sources in a simulation: ns3::Config.
*/
/**
* @ingroup core
* @defgroup debugging Debugging tools
*
* @brief Assertions, breakpoints, logging, and abnormal program termination
*/
-1658
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

-85
View File
@@ -1,85 +0,0 @@
.. ns3-theme documentation master file, created by
sphinx-quickstart on Fri Jun 15 12:53:37 2012.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
The files in this directory customize the Doxygen and Sphinx
layout and rendering to give a common look.
Design
------
The basic color scheme is taken from the ns3 homepage, http://www.nsnam.org/
Since Sphinx provides more flexible layout and configuration control,
the Doxygen layout and images are used as the basis.
The top bar design is derived from Doxygen's default when a logo,
project name and description are given in the Doxygen configuration file.
The files ``layout.html`` and ``ns3_doxy_header.html`` should kept in sync.
Sphinx Files
------------
``layout.html`` (:ref:`static filename <sphinx-static-filenames>`)
Augments the default page layout, including the top bar.
``theme.conf`` (:ref:`static filename <sphinx-static-filenames>`)
Configuration parameters for ``layout.html`` and ``default.css_t``.
``static/bc_s.png``
Angle bracket in Sphinx relbar, taken from the Doxygen html output.
``static/default.css_t`` (:ref:`static filename <sphinx-static-filenames>`)
Default Sphinx CSS template, copied from the default theme.
``static/nav_f.png``
Background image for headings, taken from the Doxygen html output.
``static/sidebar.js`` (:ref:`static filename <sphinx-static-filenames>`)
Sidebar script, copied from the default theme.
``static/tab_b.png``
Background image for the Sphinx relbar, taken from the Doxygen html output.
.. _sphinx-static-filenames:
.. note:: **Static Filenames**
Sphinx uses hard-coded file names (and the ``static`` directory name).
The files referenced above can't be renamed.
Doxygen Files
-------------
``ns3_doxy_footer.html``
Defines the page footer.
``ns3_doxy_header.html``
Defines the page header, including the top bar.
``static/doxygen.css``
Default Doxygen CSS file, obtained by the ``doxygen -w html ...``
command.
Shared Files
------------
``README.rst``
This file, not used by Doxygen or Sphinx.
``static/bar-top.png``
Background image for the top bar, resized from the ns-3 homepage.
``static/favicon.ico``
Browser location bar favicon, referenced in ``layout.html`` and
``ns3_doxy_header.html``, taken from the ns-3 homepage.
``static/ns-3-inverted-notext-small.png``
Logo used in the top bar, taken from the ns-3 media kit.
``static/ns3_stylesheet.css``
Style customizations for both Doxygen and Sphinx.
-216
View File
@@ -1,216 +0,0 @@
#!/bin/bash
# Get the current repo name and version
# to format urls appropriately via Javascript
# variables for inclusion in html files.
# Use cases:
# 1. Hosted on nsnam.org, tagged release.
# 2. Hosted on nsnam.org, ns-3-dev.
# 3. User repo, modified from a tagged release (or ns-3-dev).
# 4. User repo, at a release tag.
# 5. User repo, at a private tag.
# 6. Private web host, at a tag (or ns-3-dev, or local mod).
#
# For case 1 and 2, we want the links to point to the nsnam.org
# publicly hosted pages. For all other cases, we want to point
# to the built pages in the repo itself.
#
# For robustness, we attempt to identify cases 1 & 2
# automatically. (There is also an explicit switch '-p'
# to force us into the public case.)
#
# The approach to identify cases 1 & 2 is to test:
# a. We're on nsnam.org (actually, nsnam-www.coe-hosted.gatech.edu), and
# b. We're in the tmp build directory, /tmp/daily-nsnam/
# (This is the directory used by the update-* scripts
# run by cron jobs.)
#
# If both a and b are true, we're building for public urls.
# (The newer update-docs script (through waf) sets
# NS3_WWW_URLS=public explicitly.)
#
# The repo version is either a tag name or a commit (short) id.
#
# If we're building for nsnam.org, and at a tag, we use just
# the tag as the repo name/version string, e.g. 'ns-3.14'.
# Otherwise, we're building for ns-3-dev, and we use, e.g,
# 'ns-3-dev @ fd0f27a10eff'.
#
# If we're *not* building for nsnam.org, we use the repo
# directory name as the repo name. (This will typically be
# a name meaningful to the user doing the build, perhaps a
# shorthand for the feature they are working on.) For
# example, this script was developed in a repo named
# 'docs'. We always use the repo version, resulting
# in document version strings like 'docs @ ns-3.15' or
# 'docs @ fd0f27a10eff'
#
me=`basename $0`
function say
{
echo "$me: $*"
}
function usage
{
cat <<-EOF
Usage: $me [-p] normal versioning
$me [-n] [-d] [-t] test options
-p build public urls, NS3_WWW_URLS=public is an alternative
Testing options:
-n pretend we are on nsnam.org
-d pretend we are in the automated build directory
-t pretend we are at a repo tag
EOF
exit 1
}
# script arguments
say
say using doxygen: $(which doxygen) $(doxygen --version)
public=0
nsnam=0
daily=0
tag=0
while getopts :pndth option ; do
case $option in
(p) public=1 ;;
(n) nsnam=1 ;;
(d) daily=1 ;;
(t) tag=1 ;;
(h) usage ;;
(:) say "Missing argument to -$OPTARG" ; usage ;;
(\?) say "Invalid option: -$OPTARG" ; usage ;;
esac
done
# Hostname, fully qualified, e.g. nsnam-www.coe-hosted.gatech.edu
HOST=`hostname`
NSNAM="nsnam-www.coe-hosted.gatech.edu"
# Build directory
DAILY="^/tmp/daily-nsnam/"
if [ $nsnam -eq 1 ]; then
HOST=$NSNAM
say "-n forcing HOST = $HOST"
fi
if [ $daily -eq 1 ] ; then
OLDPWD=$PWD
PWD=/tmp/daily-nsnam/foo
say "-d forcing PWD = $PWD"
fi
if [ $tag -eq 1 ]; then
version="ns-3.14"
say "-t forcing tagged version = $version"
fi
if ((nsnam + daily + tag > 0)) ; then
say
fi
if [[ ($public == 1) || \
("${NS3_WWW_URLS:-}" == "public") || \
( ($HOST == $NSNAM) && ($PWD =~ $DAILY) ) ]] ; then
PUBLIC=1
say "building public docs for nsnam.org"
else
PUBLIC=0
say "building private docs"
fi
if [ $daily -eq 1 ]; then
PWD=$OLDPWD
fi
# Destination javascript file
outf="doc/ns3_html_theme/static/ns3_version.js"
# Distance from last tag
# Zero distance means we're at the tag
distance=`hg log -r tip --template '{latesttagdistance}'`
if [ $distance -eq 1 ]; then
version=`hg log -r tip --template '{latesttag}'`
say "at tag $version"
elif [ $tag -eq 1 ]; then
distance=1
# version previously set
vers_href=
else
version=`hg log -r tip --template '{node|short}'`
# Check for uncommitted changes
hg summary | grep 'commit:' | grep -q '(clean)'
if [ ! $? ] ; then
say "beyond latest tag, last commit: $version, dirty"
dirty="(+)"
else
say "beyond latest tag, last commit: $version, clean"
dirty=
fi
fi
if [ $PUBLIC -eq 1 ]; then
echo "// ns3_version.js: automatically generated" > $outf
echo "// public urls" >> $outf
# Generate URL relative to server root
echo "var ns3_host = \"/\";" >> $outf
if [ $distance -eq 1 ]; then
# Like "http://www.nsnam.org/ns-3-14"
vers_href="http://www.nsnam.org/ns-3-${version#ns-3.}"
vers_href="<a href=\\\"$vers_href\\\">$version$dirty</a>"
echo "var ns3_version = \"Release $vers_href\";" >> $outf
echo "var ns3_release = \"docs/release/${version#ns-}/\";" >> $outf
else
# Like "http://code.nsnam.org/ns-3-dev/rev/<hash>"
vers_href="http://code.nsnam.org/ns-3-dev/rev/$version"
version="<a href=\\\"$vers_href\\\">$version$dirty</a>"
echo "var ns3_version = \"ns-3-dev @ $version\";" >> $outf
echo "var ns3_release = \"docs/\";" >> $outf
fi
echo "var ns3_local = \"\";" >> $outf
echo "var ns3_doxy = \"doxygen/\";" >> $outf
else
repo=`basename $PWD`
echo "// ns3_version.js: automatically generated" > $outf
echo "// private urls" >> $outf
echo "var ns3_host = \"file://$PWD/\";" > $outf
echo "var ns3_version = \"$repo @ $version$dirty\";" >> $outf
echo "var ns3_release = \"doc/\";" >> $outf
echo "var ns3_local = \"build/\";" >> $outf
echo "var ns3_doxy = \"html/\";" >> $outf
fi
# Copy to html directories
# This seems not always done automatically
# by Sphinx when rebuilding
cd doc 2>&1 >/dev/null
for d in {manual,models,tutorial{,-pt-br}}/build/{single,}html/_static/ ; do
if [ ! -d $d ]; then
mkdir -p $d
fi
cp ns3_html_theme/static/ns3_version.js $d
done
cd - 2>&1 >/dev/null
# Show what was done
say
say "outf = $outf:"
cat -n $outf
say Done.
-121
View File
@@ -1,121 +0,0 @@
{#
ns-3/layout.html
~~~~~~~~~~~~~~~~~~~
Sphinx layout template for ns-3.
:copyright: Copyright 2012 by ns-3, see AUTHORS.
:license: GPL, see LICENSE for details.
#}
{% extends "basic/layout.html" %}
{% set reldelim1 = '<span class="navelem">&nbsp;</span>' %}
{# set reldelim1 = ' @' #}
{%- block extrahead %}
{%- if theme_customstylesheet %}
<link rel="stylesheet" type="text/css"
href="{{ pathto('_static/'+theme_customstylesheet, 1) }}" />
{%- endif %}
{%- if theme_favicon %}
<link rel="icon" type="image/ico"
href="{{ pathto('_static/'+theme_favicon, 1) }}" />
{%- endif %}
<script type="text/javascript" src="_static/drop-down-menu.js"></script>
<script type="text/javascript" src="_static/ns3_version.js"></script>
<script type="text/javascript">var ns3_builder="{{builder}}";</script>
<script type="text/javascript" src="_static/ns3_links.js"></script>
{% endblock %}
{% block header %}
<div id="titlearea">
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr style="height: 56px;">
<td id="projectlogo">
<a id="ns3_home1"
href="http://www.nsnam.org/">
<img alt="ns-3 Logo"
src="{{ pathto('_static/' + theme_logo, 1)}}"/>
</a>
</td>
<td id="projecttext">
<div id="projectbrief">A Discrete-Event Network Simulator</div>
<span id="projectnumber"><script type="text/javascript">document.write(ns3_version)</script></span>
</td>
<td id="ns3-menu">
<div class="menu">
<ul >
<li><a id="ns3_home2"
href="http://www.nsnam.org/"
>&nbsp;&nbsp;Home</a>
</li>
<li><span
onmouseover="mopen('mTuts')"
onmouseout="mclosetime()"
>Tutorials &nbsp;&#x25BC;</span>
<div id="mTuts"
onmouseover="mcancelclosetime()"
onmouseout="mclosetime()">
<a id="ns3_tut"
href="/docs/tutorial/html/index.html"
>English</a><br/>
<a id="ns3_ptbr"
href="/docs/tutorial-pt-br/html/index.html"
>Portuguese</a><br/>
</li>
<li><span
onmouseover="mopen('mDocs')"
onmouseout="mclosetime()"
>Docs &nbsp;&nbsp;&nbsp;&#x25BC;</span>
<div id="mDocs"
onmouseover="mcancelclosetime()"
onmouseout="mclosetime()">
<a id="ns3_wiki"
href="http://www.nsnam.org/wiki"
>Wiki</a><br/>
<a id="ns3_man"
href="/docs/manual/html/index.html"
>Manual</a><br/>
<a id="ns3_mod"
href="/docs/models/html/index.html"
>Models</a><br/>
</li>
<li><span
onmouseover="mopen('mDev')"
onmouseout="mclosetime()"
>Develop &#x25BC;</span>
<div id="mDev"
onmouseover="mcancelclosetime()"
onmouseout="mclosetime()">
<a id="ns3_api"
href="/docs/doxygen/html/index.html"
>API</a><br/>
<a id="ns3_bugs"
href="http://www.nsnam.org/bugzilla/">Bugs</a>
</li>
</ul>
</div>
</td>
<td id="projectsection">
<span style="margin-right:10px">{{ shorttitle }}</span>
</td>
</tr>
</tbody>
</table>
<script type="text/javascript">ns3_write_links()</script>
</div>
{% endblock %}
{% block rootrellink %}
<li class="navelem"><a href="{{ theme_homepage }}">{{ theme_projectname }}</a><span class="navelem">&nbsp;</span></li>
{{ super() }}
{% endblock %}
{% if theme_collapsiblesidebar|tobool %}
{% set script_files = script_files + ['_static/sidebar.js'] %}
{% endif %}
-24
View File
@@ -1,24 +0,0 @@
<!-- ns-3 doxygen header, based on
HTML header for doxygen 1.8.3.1
Verified compatible with 1.8.6
-->
<!-- start footer part -->
<!--BEGIN GENERATE_TREEVIEW-->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
<ul>
$navpath
<li class="footer">$generatedby
<a href="http://www.doxygen.org/index.html">
<img class="footer" src="$relpath$doxygen.png" alt="doxygen"/></a> $doxygenversion </li>
</ul>
</div>
<!--END GENERATE_TREEVIEW-->
<!--BEGIN !GENERATE_TREEVIEW-->
<hr class="footer"/><address class="footer"><small>
$generatedby &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="$relpath$doxygen.png" alt="doxygen"/>
</a> $doxygenversion
</small></address>
<!--END !GENERATE_TREEVIEW-->
</body>
</html>
-110
View File
@@ -1,110 +0,0 @@
<!-- ns-3 doxygen header, based on
HTML header for doxygen 1.8.3.1
Verified compatible with 1.8.6
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
<link href="$relpath$favicon.ico" rel="shortcut icon" type="image/ico"></link>
<script type="text/javascript" src="$relpath$drop-down-menu.js"></script>
<script type="text/javascript" src="$relpath$ns3_version.js"></script>
<script type="text/javascript" src="$relpath$ns3_links.js"></script>
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr style="height: 56px;">
<td id="projectlogo">
<a id="ns3_home1"
href="http://www.nsnam.org/">
<img alt="ns-3 Logo"
src="$relpath$$projectlogo"/>
</a>
</td>
<td id="projecttext">
<div id="projectbrief">A Discrete-Event Network Simulator</div>
<span id="projectnumber"><script type="text/javascript">document.write(ns3_version)</script></span>
</td>
<td id="ns3-menu">
<div class="menu">
<ul >
<li><a id="ns3_home2"
href="http://www.nsnam.org/"
>&nbsp;&nbsp;Home</a>
</li>
<li><span
onmouseover="mopen('mTuts')"
onmouseout="mclosetime()"
>Tutorials &nbsp;&#x25BC;</span>
<div id="mTuts"
onmouseover="mcancelclosetime()"
onmouseout="mclosetime()">
<a id="ns3_tut"
href="/docs/tutorial/html/index.html"
>English</a><br/>
<a id="ns3_ptbr"
href="/docs/tutorial-pt-br/html/index.html"
>Portuguese</a><br/>
</li>
<li><span
onmouseover="mopen('mDocs')"
onmouseout="mclosetime()"
>Docs &nbsp;&nbsp;&nbsp;&#x25BC;</span>
<div id="mDocs"
onmouseover="mcancelclosetime()"
onmouseout="mclosetime()">
<a id="ns3_wiki"
href="http://www.nsnam.org/wiki"
>Wiki</a><br/>
<a id="ns3_man"
href="/docs/manual/html/index.html"
>Manual</a><br/>
<a id="ns3_mod"
href="/docs/models/html/index.html"
>Models</a><br/>
</li>
<li><span
onmouseover="mopen('mDev')"
onmouseout="mclosetime()"
>Develop &#x25BC;</span>
<div id="mDev"
onmouseover="mcancelclosetime()"
onmouseout="mclosetime()">
<a id="ns3_api"
href="/docs/doxygen/index.html"
>API</a><br/>
<a id="ns3_bugs"
href="http://www.nsnam.org/bugzilla/">Bugs</a>
</li>
</ul>
</div>
</td>
<td id="projectsection">
<span style="margin-right:10px">API</span>
</td>
</tr>
</tbody>
</table>
<script type="text/javascript">ns3_write_links();</script>
</div>
<!--END TITLEAREA-->
<!-- end header part -->
Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 635 B

-310
View File
@@ -1,310 +0,0 @@
/*
* default.css_t
* ~~~~~~~~~~~~~
*
* Sphinx stylesheet -- default theme.
*
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: {{ theme_bodyfont }};
font-size: 100%;
background-color: {{ theme_footerbgcolor }};
color: #000;
margin: 0;
padding: 0;
}
div.document {
background-color: {{ theme_sidebarbgcolor }};
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 {{ theme_sidebarwidth|toint }}px;
}
div.body {
background-color: {{ theme_bgcolor }};
color: {{ theme_textcolor }};
padding: 0 20px 30px 20px;
}
{%- if theme_rightsidebar|tobool %}
div.bodywrapper {
margin: 0 {{ theme_sidebarwidth|toint }}px 0 0;
}
{%- endif %}
div.footer {
color: {{ theme_footertextcolor }};
width: 100%;
padding: 9px 0 9px 0;
text-align: center;
font-size: 75%;
}
div.footer a {
color: {{ theme_footertextcolor }};
text-decoration: underline;
}
div.related {
background-color: {{ theme_relbarbgcolor }};
line-height: 30px;
color: {{ theme_relbartextcolor }};
}
div.related a {
color: {{ theme_relbarlinkcolor }};
}
div.sphinxsidebar {
{%- if theme_stickysidebar|tobool %}
top: 30px;
bottom: 0;
margin: 0;
position: fixed;
overflow: auto;
height: auto;
{%- endif %}
{%- if theme_rightsidebar|tobool %}
float: right;
{%- if theme_stickysidebar|tobool %}
right: 0;
{%- endif %}
{%- endif %}
}
{%- if theme_stickysidebar|tobool %}
/* this is nice, but it it leads to hidden headings when jumping
to an anchor */
/*
div.related {
position: fixed;
}
div.documentwrapper {
margin-top: 30px;
}
*/
{%- endif %}
div.sphinxsidebar h3 {
font-family: {{ theme_headfont }};
color: {{ theme_sidebartextcolor }};
font-size: 1.4em;
font-weight: normal;
margin: 0;
padding: 0;
}
div.sphinxsidebar h3 a {
color: {{ theme_sidebartextcolor }};
}
div.sphinxsidebar h4 {
font-family: {{ theme_headfont }};
color: {{ theme_sidebartextcolor }};
font-size: 1.3em;
font-weight: normal;
margin: 5px 0 0 0;
padding: 0;
}
div.sphinxsidebar p {
color: {{ theme_sidebartextcolor }};
}
div.sphinxsidebar p.topless {
margin: 5px 10px 10px 10px;
}
div.sphinxsidebar ul {
margin: 10px;
padding: 0;
color: {{ theme_sidebartextcolor }};
}
div.sphinxsidebar a {
color: {{ theme_sidebarlinkcolor }};
}
div.sphinxsidebar input {
border: 1px solid {{ theme_sidebarlinkcolor }};
font-family: sans-serif;
font-size: 1em;
}
{% if theme_collapsiblesidebar|tobool %}
/* for collapsible sidebar */
div#sidebarbutton {
background-color: {{ theme_sidebarbtncolor }};
}
{% endif %}
/* -- hyperlink styles ------------------------------------------------------ */
a {
color: {{ theme_linkcolor }};
text-decoration: none;
}
a:visited {
color: {{ theme_visitedlinkcolor }};
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
{% if theme_externalrefs|tobool %}
a.external {
text-decoration: none;
border-bottom: 1px dashed {{ theme_linkcolor }};
}
a.external:hover {
text-decoration: none;
border-bottom: none;
}
a.external:visited {
text-decoration: none;
border-bottom: 1px dashed {{ theme_visitedlinkcolor }};
}
{% endif %}
/* -- body styles ----------------------------------------------------------- */
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: {{ theme_headfont }};
background-color: {{ theme_headbgcolor }};
font-weight: normal;
color: {{ theme_headtextcolor }};
border-bottom: 1px solid #ccc;
margin: 20px -20px 10px -20px;
padding: 3px 0 3px 10px;
}
div.body h1 { margin-top: 0; font-size: 200%; }
div.body h2 { font-size: 160%; }
div.body h3 { font-size: 140%; }
div.body h4 { font-size: 120%; }
div.body h5 { font-size: 110%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: {{ theme_headlinkcolor }};
font-size: 0.8em;
padding: 0 4px 0 4px;
text-decoration: none;
}
a.headerlink:hover {
background-color: {{ theme_headlinkcolor }};
color: white;
}
div.body p, div.body dd, div.body li {
text-align: justify;
line-height: 130%;
}
div.admonition p.admonition-title + p {
display: inline;
}
div.admonition p {
margin-bottom: 5px;
}
div.admonition pre {
margin-bottom: 5px;
}
div.admonition ul, div.admonition ol {
margin-bottom: 5px;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
div.warning {
background-color: #ffe4e4;
border: 1px solid #f66;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre {
padding: 5px;
background-color: {{ theme_codebgcolor }};
color: {{ theme_codetextcolor }};
line-height: 120%;
border: 1px solid #ac9;
border-left: none;
border-right: none;
}
tt {
background-color: #ecf0f3;
padding: 0 1px 0 1px;
font-size: 0.95em;
}
th {
background-color: #ede;
}
.warning tt {
background: #efc2c2;
}
.note tt {
background: #d6d6d6;
}
.viewcode-back {
font-family: {{ theme_bodyfont }};
}
div.viewcode-block:target {
background-color: #f4debf;
border-top: 1px solid #ac9;
border-bottom: 1px solid #ac9;
}
@@ -1,45 +0,0 @@
// Copyright 2006-2007 javascript-array.com
// http://javascript-array.com/scripts/simple_drop_down_menu/
var timeout = 250;
var closetimer = 0;
var ddmenuitem = 0;
// open hidden layer
function mopen(id)
{
// cancel close timer
mcancelclosetime();
// close old layer
if(ddmenuitem) ddmenuitem.style.visibility = 'hidden';
// get new layer and show it
ddmenuitem = document.getElementById(id);
ddmenuitem.style.visibility = 'visible';
}
// close showed layer
function mclose()
{
if(ddmenuitem) ddmenuitem.style.visibility = 'hidden';
}
// go close timer
function mclosetime()
{
closetimer = window.setTimeout(mclose, timeout);
}
// cancel close timer
function mcancelclosetime()
{
if(closetimer)
{
window.clearTimeout(closetimer);
closetimer = null;
}
}
// close layer when click-out
document.onclick = mclose;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Some files were not shown because too many files have changed in this diff Show More