--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tutorial/tracing.texi Fri Oct 09 19:44:13 2009 +0200
@@ -0,0 +1,1105 @@
+
+@c ============================================================================
+@c Begin document body here
+@c ============================================================================
+
+@c ============================================================================
+@c PART: The Tracing System
+@c ============================================================================
+@c The below chapters are under the major heading "The Tracing System"
+@c This is similar to the Latex \part command
+@c
+@c ============================================================================
+@c The Tracing System
+@c ============================================================================
+@node The Tracing System
+@chapter The Tracing System
+
+@menu
+* Background::
+@end menu
+
+@c ============================================================================
+@c Background
+@c ============================================================================
+@node Background
+@section Background
+
+As mentioned in the Using the Tracing System section, the whole point of running
+an @code{ns-3} simulation is to generate output for study. You have two basic
+strategies to work with in @code{ns-3}: using generic pre-defined bulk output
+mechanisms and parsing their content to extract interesting information; or
+somehow developing an output mechanism that conveys exactly (and perhaps only)
+the information wanted.
+
+Using pre-defined bulk output mechansims has the advantage of not requiring any
+changes to @code{ns-3}, but it does require programming. Often, pcap or NS_LOG
+output messages are gathered during simulation runs and separately run through
+scripts that use grep, sed or awk to parse the messages and reduce and transform
+the data to a manageable form. Programs must be written to do the
+transformation, so this does not come for free. Of course, if the information
+of interest in does not exist in any of the pre-defined output mechanisms,
+this approach fails.
+
+If you need to add some tidbit of information to the pre-defined bulk mechanisms,
+this can certainly be done; and if you use one of the @code{ns-3} mechanisms,
+you may get your code added as a contribution.
+
+@code{ns-3} provides another mechanism, called Tracing, that avoids some of the
+problems inherent in the bulk output mechanisms. It has several important
+advantages. First, you can reduce the amount of data you have to manage by only
+tracing the events of interest to you. Second, if you use this method, you can
+control the format of the output directly so you avoid the postprocessing step
+with sed or awk script. If you desire, your output can be formatted directly into
+a form acceptable by gnuplot, for example. You can add hooks in the core which
+can then be accessed by other users, but which will produce no information unless
+explicitly asked to do so. For these reasons, we believe that the @code{ns-3}
+Tracing system is the best way to get information out of a simulation and is also
+therefore one of the most important mechanisms to understand in @command{ns-3}.
+
+@subsection Blunt Instruments
+There are many ways to get information out of a program. The most
+straightforward way is to just directly print the information to the standard
+output, as in,
+
+@verbatim
+ #include <iostream>
+ ...
+ void
+ SomeFunction (void)
+ {
+ uint32_t x = SOME_INTERESTING_VALUE;
+ ...
+ std::cout << "The value of x is " << x << std::endl;
+ ...
+ }
+@end verbatim
+
+Nobody is going to prevent you from going deep into the core of @code{ns-3} and
+adding print statements. This is insanely easy to do and, after all, you have
+complete control of your own @code{ns-3} branch. This will probably not turn
+out to be very satisfactory in the long term, though.
+
+As the number of print statements increases in your programs, the task of
+dealing with the large number of outputs will become more and more complicated.
+Eventually, you may feel the need to control what information is being printed
+in some way; perhaps by turning on and off certain categories of prints, or
+increasing or decreasing the amount of information you want. If you continue
+down this path you may discover that you have re-implemented the @code{NS_LOG}
+mechanism. In order to avoid that, one of the first things you might consider
+is using @code{NS_LOG} itself.
+
+We mentioned above that one way to get information out of @code{ns-3} is to
+parse existing NS_LOG output for interesting information. If you discover that
+some tidbit of information you need is not present in existing log output, you
+could edit the core of @code{ns-3} and simply add your interesting information
+to the output stream. Now, this is certainly better than adding your own
+print statements since it follows @code{ns-3} coding conventions and could
+potentially be useful to other people as a patch to the existing core.
+
+Let's pick a random example. If you wanted to add more logging to the
+@code{ns-3} TCP socket (@code{tcp-socket-impl.cc}) you could just add a new
+message down in the implementation. Notice that in TcpSocketImpl::ProcessAction()
+there is no log message for the @code{ACK_TX} case. You could simply add one,
+changing the code from:
+
+@verbatim
+ bool TcpSocketImpl::ProcessAction (Actions_t a)
+ { // These actions do not require a packet or any TCP Headers
+ NS_LOG_FUNCTION (this << a);
+ switch (a)
+ {
+ case NO_ACT:
+ NS_LOG_LOGIC ("TcpSocketImpl " << this <<" Action: NO_ACT");
+ break;
+ case ACK_TX:
+ SendEmptyPacket (TcpHeader::ACK);
+ break;
+ ...
+@end verbatim
+
+to add a new @code{NS_LOG_LOGIC} in the appropriate @code{case} statement:
+
+@verbatim
+ bool TcpSocketImpl::ProcessAction (Actions_t a)
+ { // These actions do not require a packet or any TCP Headers
+ NS_LOG_FUNCTION (this << a);
+ switch (a)
+ {
+ case NO_ACT:
+ NS_LOG_LOGIC ("TcpSocketImpl " << this << " Action: NO_ACT");
+ break;
+ case ACK_TX:
+ NS_LOG_LOGIC ("TcpSocketImpl " << this << " Action: ACK_TX");
+ SendEmptyPacket (TcpHeader::ACK);
+ break;
+ ...
+@end verbatim
+
+This may seem fairly simple and satisfying at first glance, but something to
+consider is that you will be writing code to add the @code{NS_LOG} statement
+and you will also have to write code (as in grep, sed or awk scripts) to parse
+the log output in order to isolate your information. This is because even
+though you have some control over what is output by the logging system, you
+only have control down to the log component level.
+
+If you are adding code to an existing module, you will also have to live with the
+output that every other developer has found interesting. You may find that in
+order to get the small amount of information you need, you may have to wade
+through huge amounts of extraneous messages that are of no interest to you. You
+may be forced to save huge log files to disk and process them down to a few lines
+whenever you want to do anything.
+
+Since there are no guarantees in @code{ns-3} about the stability of @code{NS_LOG}
+messages, you may also discover that pieces of log output on which you depend
+disappear or change between releases. If you depend on the structure of the
+output, you may find other messages being added or deleted which may affect your
+parsing code.
+
+For these reasons, we consider prints to @code{std::cout} and NS_LOG messages
+simple ways to get more information out of @code{ns-3}, but they are really
+unstable and quite blunt instruments.
+
+It is desirable to have a stable facility using stable APIs that allow one to
+reach into the core system and only get the information required. It is
+desirable to be able to do this without having to change and recompile the
+core system. Even better would be a system that notified the user when an item
+of interest changed or an interesting event happened so the user doesn't have
+to actively go poke around in the system looking for things.
+
+The @command{ns-3} tracing system is designed to work along those lines and is
+well-integrated with the Attribute and Config subsystems allowing for relatively
+simple use scenarios.
+
+@node Overview
+@section Overview
+
+The ns-3 tracing system is built on the concepts of independent tracing sources
+and tracing sinks; along with a uniform mechanism for connecting sources to sinks.
+
+Trace sources are entities that can signal events that happen in a simulation and
+provide access to interesting underlying data. For example, a trace source could
+indicate when a packet is received by a net device and provide access to the
+packet contents for interested trace sinks. A trace source might also indicate
+when an iteresting state change happens in a model. For example, the congestion
+window of a TCP model is a prime candidate for a trace source.
+
+Trace sources are not useful by themselves; they must be connected to other pieces
+of code that actually do something useful with the information provided by the source.
+The entities that consume trace information are called trace sinks. Trace sources
+are generators of events and trace sinks are consumers. This explicit division
+allows for large numbers of trace sources to be scattered around the system in
+places which model authors believe might be useful.
+
+There can be zero or more consumers of trace events generated by a trace source.
+One can think of a trace source as a kind of point-to-multipoint information link.
+Your code looking for trace events from a particular piece of core code could
+happily coexist with other code doing something entirely different from the same
+information.
+
+Unless a user connects a trace sink to one of these sources, nothing is output. By
+using the tracing system, both you and other people at the same trace source are
+getting exactly what they want and only what they want out of the system. Neither
+of you are impacting any other user by changing what information is output by the
+system. If you happen to add a trace source, your work as a good open-source
+citizen may allow other users to provide new utilities that are perhaps very useful
+overall, without making any changes to the @code{ns-3} core.
+
+@node A Simple Low-Level Example
+@subsection A Simple Low-Level Example
+
+Let's take a few minutes and walk through a simple tracing example. We are going
+to need a little background on Callbacks to understand what is happening in the
+example, so we have to take a small detour right away.
+
+@node Callbacks
+@subsubsection Callbacks
+
+The goal of the Callback system in @code{ns-3} 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,
+
+@verbatim
+ int (*pfi)(int arg) = 0;
+@end verbatim
+
+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, you could
+provide a function that looks like,
+
+@verbatim
+ int MyFunction (int arg) {}
+@end verbatim
+
+If you have this target, you can initialize the variable to point to your
+function:
+
+@verbatim
+ pfi = MyFunction;
+@end verbatim
+
+You can then call MyFunction indirectly using the more suggestive form of
+the call,
+
+@verbatim
+ int result = (*pfi) (1234);
+@end verbatim
+
+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,
+
+@verbatim
+ int result = pfi (1234);
+@end verbatim
+
+This looks like you are calling a function named ``pfi,'' but the compiler is
+smart enough to know to call through the variable @code{pfi} indirectly to
+the function @code{MyFunction}.
+
+Conceptually, this is almost exactly how the tracing system will work.
+Basically, a trace source @emph{is} a callback. When a trace sink expresses
+interest in receiving trace events, it adds a Callback to a list of Callbacks
+internally held by the trace source. When an interesting event happens, the
+trace source invokes its @code{operator()} providing zero or more parameters.
+The @code{operator()} eventually wanders down into the system and does something
+remarkably like the indirect call you just saw. It provides zero or more
+parameters (the call to ``pfi'' above passed one parameter to the target function
+@code{MyFunction}.
+
+The important difference that the tracing system adds is that for each trace
+source there is an internal list of Callbacks. Instead of just making one
+indirect call, a trace source may invoke any number of Callbacks. When a trace
+sink expresses interest in notifications from a trace source, it basically just
+arranges to add its own function to the callback list.
+
+If you are interested in more details about how this is actually arranged in
+@code{ns-3}, feel free to peruse the Callback section of the manual.
+
+@node Example Code
+@subsubsection Example Code
+
+We have provided some code to implement what is really the simplest example
+of tracing that can be assembled. You can find this code in the tutorial
+directory as @code{fourth.cc}. Let's walk through it.
+
+@verbatim
+ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+ /*
+ * 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
+ */
+
+ #include "ns3/object.h"
+ #include "ns3/uinteger.h"
+ #include "ns3/traced-value.h"
+ #include "ns3/trace-source-accessor.h"
+
+ #include <iostream>
+
+ using namespace ns3;
+@end verbatim
+
+Most of this code should be quite familiar to you. As mentioned above, the
+trace system makes heavy use of the Object and Attribute systems, so you will
+need to include them. The first two includes above bring in the declarations
+for those systems explicitly. You could use the core module header, but this
+illustrates how simple this all really is.
+
+The file, @code{traced-value.h} brings in the required declarations for tracing
+of data that obeys value semantics. In general, value semantics just means that
+you can pass the object around, not an address. In order to use value semantics
+at all you have to have an object with an associated copy constructor and
+assignment operator available. We extend the requirements to talk about the set
+of operators that are pre-defined for plain-old-data (POD) types. Operator=,
+operator++, operator---, operator+, operator==, etc.
+
+What this all really means is that you will be able to trace changes to a C++
+object made using those operators.
+
+Since the tracing system is integrated with Attributes, and Attributes work
+with Objects, there must be an @command{ns-3} @code{Object} for the trace source
+to live in. The next code snippet declares and defines a simple Object we can
+work with.
+
+@verbatim
+ class MyObject : public Object
+ {
+ public:
+ static TypeId GetTypeId (void)
+ {
+ static TypeId tid = TypeId ("MyObject")
+ .SetParent (Object::GetTypeId ())
+ .AddConstructor<MyObject> ()
+ .AddTraceSource ("MyInteger",
+ "An integer value to trace.",
+ MakeTraceSourceAccessor (&MyObject::m_myInt))
+ ;
+ return tid;
+ }
+
+ MyObject () {}
+ TracedValue<int32_t> m_myInt;
+ };
+@end verbatim
+
+The two important lines of code, above, with respect to tracing are the
+@code{.AddTraceSource} and the @code{TracedValue} declaration of @code{m_myInt}.
+
+The @code{.AddTraceSource} provides the ``hooks'' used for connecting the trace
+source to the outside world through the config system. The @code{TracedValue}
+declaration provides the infrastructure that overloads the operators mentioned
+above and drives the callback process.
+
+@verbatim
+ void
+ IntTrace (int32_t oldValue, int32_t newValue)
+ {
+ std::cout << "Traced " << oldValue << " to " << newValue << std::endl;
+ }
+@end verbatim
+
+This is the definition of the trace sink. It corresponds directly to a callback
+function. Once it is connected, this function will be called whenever one of the
+overloaded operators of the @code{TracedValue} is executed.
+
+We have now seen the trace source and the trace sink. What remains is code to
+connect the source to the sink.
+
+@verbatim
+ int
+ main (int argc, char *argv[])
+ {
+ Ptr<MyObject> myObject = CreateObject<MyObject> ();
+ myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback(&IntTrace));
+
+ myObject->m_myInt = 1234;
+ }
+@end verbatim
+
+Here we first create the Object in which the trace source lives.
+
+The next step, the @code{TraceConnectWithoutContext}, forms the connection
+between the trace source and the trace sink. Notice the @code{MakeCallback}
+template function. This function does the magic required to create the
+underlying @code{ns-3} Callback object and associate it with the function
+@code{IntTrace}. TraceConnect makes the association between your provided
+function and the overloaded @code{operator()} in the traced variable referred
+to by the ``MyInteger'' Attribute. After this association is made, the trace
+source will ``fire'' your provided callback function.
+
+The code to make all of this happen is, of course, non-trivial, but the essence
+is that you are arranging for something that looks just like the @code{pfi()}
+example above to be called by the trace source. The declaration of the
+@code{TracedValue<int32_t> m_myInt;} in the Object itself performs the magic
+needed to provide the overloaded operators (++, ---, etc.) that will use the
+@code{operator()} to actually invoke the Callback with the desired parameters.
+The @code{.AddTraceSource} performs the magic to connect the Callback to the
+Config system, and @code{TraceConnectWithoutContext} performs the magic to
+connect your function to the trace source, which is specified by Attribute
+name.
+
+Let's ignore the bit about context for now.
+
+Finally, the line,
+
+@verbatim
+ myObject->m_myInt = 1234;
+@end verbatim
+
+should be interpreted as an invocation of @code{operator=} on the member
+variable @code{m_myInt} with the integer @code{1234} passed as a parameter.
+
+It turns out that this operator is defined (by @code{TracedValue}) to execute
+a callback that returns void and takes two integer values as parameters ---
+an old value and a new value for the integer in question. That is exactly
+the function signature for the callback function we provided --- @code{IntTrace}.
+
+To summarize, a trace source is, in essence, a variable that holds a list of
+callbacks. A trace sink is a function used as the target of a callback. The
+Attribute and object type information systems are used to provide a way to
+connect trace sources to trace sinks. The act of ``hitting'' a trace source
+is executing an operator on the trace source which fires callbacks. This
+results in the trace sink callbacks registering interest in the source being
+called with the parameters provided by the source.
+
+If you now build and run this example,
+
+@verbatim
+ ./waf --run fourth
+@end verbatim
+
+you will see the output from the @code{IntTrace} function execute as soon as the
+trace source is hit:
+
+@verbatim
+ Traced 0 to 1234
+@end verbatim
+
+When we executed the code, @code{myObject->m_myInt = 1234;}, the trace source
+fired and automatically provided the before and after values to the trace sink.
+The function @code{IntTrace} then printed this to the standard output. No
+problem.
+
+@subsection Using the Config Subsystem to Connect to Trace Sources
+
+The @code{TraceConnectWithoutContext} call shown above in the simple example is
+actually very rarely used in the system. More typically, the @code{Config}
+subsystem is used to allow selecting a trace source in the system using what is
+called a @emph{config path}. We saw an example of this in the previous section
+where we hooked the ``CourseChange'' event when we were playing with
+@code{third.cc}.
+
+Recall that we defined a trace sink to print course change information from the
+mobility models of our simulation. It should now be a lot more clear to you
+what this function is doing.
+
+@verbatim
+ void
+ CourseChange (std::string context, Ptr<const MobilityModel> model)
+ {
+ Vector position = model->GetPosition ();
+ NS_LOG_UNCOND (context <<
+ " x = " << position.x << ", y = " << position.y);
+ }
+@end verbatim
+
+When we connected the ``CourseChange'' trace source to the above trace sink,
+we used what is called a ``Config Path'' to specify the source when we
+arranged a connection between the pre-defined trace source and the new trace
+sink:
+
+@verbatim
+ std::ostringstream oss;
+ oss <<
+ "/NodeList/" << wifiStaNodes.Get (nWifi - 1)->GetId () <<
+ "/$ns3::MobilityModel/CourseChange";
+
+ Config::Connect (oss.str (), MakeCallback (&CourseChange));
+@end verbatim
+
+Let's try and make some sense of what is sometimes considered relatively
+mysterious code. For the purposes of discussion, assume that the node
+number returned by the @code{GetId()} is ``7''. In this case, the path
+above turns out to be,
+
+@verbatim
+ "/NodeList/7/$ns3::MobilityModel/CourseChange"
+@end verbatim
+
+The last segment of a config path must be an @code{Attribute} of an
+@code{Object}. In fact, if you had a pointer to the @code{Object} that has the
+``CourseChange'' @code{Attribute} handy, you could write this just like we did
+in the previous example. You know by now that we typically store pointers to
+our nodes in a NodeContainer. In the @code{third.cc} example, the Nodes of
+interest are stored in the @code{wifiStaNodes} NodeContainer. In fact, while
+putting the path together, we used this container to get a Ptr<Node> which we
+used to call GetId() on. We could have used this Ptr<Node> directly to call
+a connect method directly:
+
+@verbatim
+ Ptr<Object> theObject = wifiStaNodes.Get (nWifi - 1);
+ theObject->TraceConnectWithoutContext ("CourseChange", MakeCallback (&CourseChange));
+@end verbatim
+
+In the @code{third.cc} example, we actually want an additional ``context'' to
+be delivered along with the Callback parameters (which will be explained below) so we
+could actually use the following equivalent code,
+
+@verbatim
+ Ptr<Object> theObject = wifiStaNodes.Get (nWifi - 1);
+ theObject->TraceConnect ("CourseChange", MakeCallback (&CourseChange));
+@end verbatim
+
+It turns out that the internal code for @code{Config::ConnectWithoutContext} and
+@code{Config::Connect} actually do find a Ptr<Object> and call the appropriate
+TraceConnect method at the lowest level.
+
+The @code{Config} functions take a path that represents a chain of @code{Object}
+pointers. Each segment of a path corresponds to an Object Attribute. The last
+segment is the Attribute of interest, and prior segments must be typed to contain
+or find Objects. The @code{Config} code parses and ``walks'' this path until it
+gets to the final segment of the path. It then interprets the last segment as
+an @code{Attribute} on the last Object it found while walking the path. The
+@code{Config} functions then call the appropriate @code{TraceConnect} or
+@code{TraceConnectWithoutContext} method on the final Object. Let's see what
+happens in a bit more detail when the above path is walked.
+
+The leading ``/'' character in the path refers to a so-called namespace. One
+of the predefined namespaces in the config system is ``NodeList'' which is a
+list of all of the nodes in the simulation. Items in the list are referred to
+by indices into the list, so ``/NodeList/7'' refers to the eighth node in the
+list of nodes created during the simulation. This reference is actually a
+@code{Ptr<Node>} and so is a subclass of an @code{ns3::Object}.
+
+As described in the Object Model section of the @code{ns-3} manual, we support
+Object Aggregation. This allows us to form an association between different
+Objects without any programming. Each Object in an Aggregation can be reached
+from the other Objects.
+
+The next path segment being walked begins with the ``$'' character. This
+indicates to the config system that a @code{GetObject} call should be made
+looking for the type that follows. It turns out that the MobilityHelper used in
+@code{third.cc} arranges to Aggregate, or associate, a mobility model to each of
+the wireless Nodes. When you add the ``$'' you are asking for another Object that
+has presumably been previously aggregated. You can think of this as switching
+pointers from the original Ptr<Node> as specified by ``/NodeList/7'' to its
+associated mobility model --- which is of type ``$ns3::MobilityModel''. If you
+are familiar with @code{GetObject}, we have asked the system to do the following:
+
+@verbatim
+ Ptr<MobilityModel> mobilityModel = node->GetObject<MobilityModel> ()
+@end verbatim
+
+We are now at the last Object in the path, so we turn our attention to the
+Attributes of that Object. The @code{MobilityModel} class defines an Attribute
+called ``CourseChange.'' You can see this by looking at the source code in
+@code{src/mobility/mobility-model.cc} and searching for ``CourseChange'' in your
+favorite editor. You should find,
+
+@verbatim
+ .AddTraceSource (``CourseChange'',
+ ``The value of the position and/or velocity vector changed'',
+ MakeTraceSourceAccessor (&MobilityModel::m_courseChangeTrace))
+@end verbatim
+
+which should look very familiar at this point.
+
+If you look for the corresponding declaration of the underlying traced variable
+in @code{mobility-model.h} you will find
+
+@verbatim
+ TracedCallback<Ptr<const MobilityModel> > m_courseChangeTrace;
+@end verbatim
+
+The type declaration @code{TracedCallback} identifies @code{m_courseChangeTrace}
+as a special list of Callbacks that can be hooked using the Config functions
+described above.
+
+The @code{MobilityModel} class is designed to be a base class providing a common
+interface for all of the specific subclasses. If you search down to the end of
+the file, you will see a method defined called @code{NotifyCourseChange()}:
+
+@verbatim
+ void
+ MobilityModel::NotifyCourseChange (void) const
+ {
+ m_courseChangeTrace(this);
+ }
+@end verbatim
+
+Derived classes will call into this method whenever they do a course change to
+support tracing. This method invokes @code{operator()} on the underlying
+@code{m_courseChangeTrace}, which will, in turn, invoke all of the registered
+Callbacks, calling all of the trace sinks that have registered interest in the
+trace source by calling a Config function.
+
+So, in the @code{third.cc} example we looked at, whenever a course change is
+made in one of the @code{RandomWalk2dMobilityModel} instances installed, there
+will be a @code{NotifyCourseChange()} call which calls up into the
+@code{MobilityModel} base class. As seen above, this invokes @code{operator()}
+on @code{m_courseChangeTrace}, which in turn, calls any registered trace sinks.
+In the example, the only code registering an interest was the code that provided
+the config path. Therefore, the @code{CourseChange} function that was hooked
+from Node number seven will be the only Callback called.
+
+The final piece of the puzzle is the ``context.'' Recall that we saw an output
+looking something like the following from @code{third.cc}:
+
+@verbatim
+ /NodeList/7/$ns3::MobilityModel/CourseChange x = 7.27897, y = 2.22677
+@end verbatim
+
+The first part of the output is the context. It is simply the path through
+which the config code located the trace source. In the case we have been looking at
+there can be any number of trace sources in the system corresponding to any number
+of nodes with mobility models. There needs to be some way to identify which trace
+source is actually the one that fired the Callback. An easy way is to request a
+trace context when you @code{Config::Connect}.
+
+@subsection How to Find and Connect Trace Sources, and Discover Callback Signatures
+
+The first question that inevitably comes up for new users of the Tracing system is,
+``okay, I know that there must be trace sources in the simulation core, but how do
+I find out what trace sources are available to me''?
+
+The second question is, ``okay, I found a trace source, how do I figure out the
+config path to use when I connect to it''?
+
+The third question is, ``okay, I found a trace source, how do I figure out what
+the return type and formal arguments of my callback function need to be''?
+
+The fourth question is, ``okay, I typed that all in and got this incredibly bizarre
+error message, what in the world does it mean''?
+
+@subsection What Trace Sources are Available?
+
+The answer to this question is found in the @code{ns-3} Doxygen. Go to the
+@code{ns-3} web site @uref{http://www.nsnam.org/getting_started.html,,``here''}
+and select the ``Doxygen (stable)'' link ``Documentation'' on the navigation
+bar to the left side of the page. Expand the ``Modules'' book in the NS-3
+documentation tree a the upper left by clicking the ``+'' box. Now, expand
+the ``Core'' book in the tree by clicking its ``+'' box. You should now
+see three extremely useful links:
+
+@itemize @bullet
+@item The list of all trace sources
+@item The list of all attributes
+@item The list of all global values
+@end itemize
+
+The list of interest to us here is ``the list of all trace sources.'' Go
+ahead and select that link. You will see, perhaps not too surprisingly, a
+list of all of the trace sources available in the @code{ns-3} core.
+
+As an example, scroll down to @code{ns3::MobilityModel}. You will find
+an entry for
+
+@verbatim
+ CourseChange: The value of the position and/or velocity vector changed
+@end verbatim
+
+You should recognize this as the trace source we used in the @code{third.cc}
+example. Perusing this list will be helpful.
+
+@subsection What String do I use to Connect?
+
+The easiest way to do this is to grep around in the @code{ns-3} codebase for someone
+who has already figured it out, You should always try to copy someone else's
+working code before you start to write your own. Try something like:
+
+@verbatim
+ find . -name '*.cc' | xargs grep CourseChange | grep Connect
+@end verbatim
+
+and you may find your answer along with working code. For example, in this
+case, @code{./ns-3-dev/examples/wireless/mixed-wireless.cc} has something
+just waiting for you to use:
+
+@verbatim
+ Config::Connect (``/NodeList/*/$ns3::MobilityModel/CourseChange'',
+ MakeCallback (&CourseChangeCallback));
+@end verbatim
+
+If you cannot find any examples in the distribution, you can find this out
+from the @code{ns-3} Doxygen. It will probably be simplest just to walk
+through the ``CourseChanged'' example.
+
+Let's assume that you have just found the ``CourseChanged'' trace source in
+``The list of all trace sources'' and you want to figure out how to connect to
+it. You know that you are using (again, from the @code{third.cc} example) an
+@code{ns3::RandomWalk2dMobilityModel}. So open the ``Class List'' book in
+the NS-3 documentation tree by clicking its ``+'' box. You will now see a
+list of all of the classes in @code{ns-3}. Scroll down until you see the
+entry for @code{ns3::RandomWalk2dMobilityModel} and follow that link.
+You should now be looking at the ``ns3::RandomWalk2dMobilityModel Class
+Reference.''
+
+If you now scroll down to the ``Member Function Documentation'' section, you
+will see documentation for the @code{GetTypeId} function. You constructed one
+of these in the simple tracing example above:
+
+@verbatim
+ static TypeId GetTypeId (void)
+ {
+ static TypeId tid = TypeId ("MyObject")
+ .SetParent (Object::GetTypeId ())
+ .AddConstructor<MyObject> ()
+ .AddTraceSource ("MyInteger",
+ "An integer value to trace.",
+ MakeTraceSourceAccessor (&MyObject::m_myInt))
+ ;
+ return tid;
+ }
+@end verbatim
+
+As mentioned above, this is the bit of code that connected the Config
+and Attribute systems to the underlying trace source. This is also the
+place where you should start looking for information about the way to
+connect.
+
+You are looking at the same information for the RandomWalk2dMobilityModel; and
+the information you want is now right there in front of you in the Doxygen:
+
+@verbatim
+ This object is accessible through the following paths with Config::Set and Config::Connect:
+
+ /NodeList/[i]/$ns3::MobilityModel/$ns3::RandomWalk2dMobilityModel
+@end verbatim
+
+The documentation tells you how to get to the @code{RandomWalk2dMobilityModel}
+Object. Compare the string above with the string we actually used in the
+example code:
+
+@verbatim
+ "/NodeList/7/$ns3::MobilityModel"
+@end verbatim
+
+The difference is due to the fact that two @code{GetObject} calls are implied
+in the string found in the documentation. The first, for @code{$ns3::MobilityModel}
+will query the aggregation for the base class. The second implied
+@code{GetObject} call, for @code{$ns3::RandomWalk2dMobilityModel}, is used to ``cast''
+the base class to the concrete imlementation class. The documentation shows
+both of these operations for you. It turns out that the actual Attribute you are
+going to be looking for is found in the base class as we have seen.
+
+Look further down in the @code{GetTypeId} doxygen. You will find,
+
+@verbatim
+ No TraceSources defined for this type.
+ TraceSources defined in parent class ns3::MobilityModel:
+
+ CourseChange: The value of the position and/or velocity vector changed
+ Reimplemented from ns3::MobilityModel
+@end verbatim
+
+This is exactly what you need to know. The trace source of interest is found in
+@code{ns3::MobilityModel} (which you knew anyway). The interesting thing this
+bit of Doxygen tells you is that you don't need that extra cast in the config
+path above to get to the concrete class, since the trace source is actually in
+the base class. Therefore the additional @code{GetObject} is not required and
+you simply use the path:
+
+@verbatim
+ /NodeList/[i]/$ns3::MobilityModel
+@end verbatim
+
+which perfectly matches the example path:
+
+@verbatim
+ /NodeList/7/$ns3::MobilityModel
+@end verbatim
+
+@subsection What Return Value and Formal Arguments?
+
+The easiest way to do this is to grep around in the @code{ns-3} codebase for someone
+who has already figured it out, You should always try to copy someone else's
+working code. Try something like:
+
+@verbatim
+ find . -name '*.cc' | xargs grep CourseChange | grep Connect
+@end verbatim
+
+and you may find your answer along with working code. For example, in this
+case, @code{./ns-3-dev/examples/wireless/mixed-wireless.cc} has something
+just waiting for you to use. You will find
+
+@verbatim
+ Config::Connect (``/NodeList/*/$ns3::MobilityModel/CourseChange'',
+ MakeCallback (&CourseChangeCallback));
+@end verbatim
+
+as a result of your grep. The @code{MakeCallback} should indicate to you that
+there is a callback function there which you can use. Sure enough, there is:
+
+@verbatim
+ static void
+ CourseChangeCallback (std::string path, Ptr<const MobilityModel> model)
+ {
+ ...
+ }
+@end verbatim
+
+@subsubsection Take my Word for It
+
+If there are no examples to work from, this can be, well, challenging to
+actually figure out from the source code.
+
+Before embarking on a walkthrough of the code, I'll be kind and just tell you
+a simple way to figure this out: The return value of your callback will always
+be void. The formal parameter list for a @code{TracedCallback} can be found
+from the template parameter list in the declaration. Recall that for our
+current example, this is in @code{mobility-model.h}, where we have previously
+found:
+
+@verbatim
+ TracedCallback<Ptr<const MobilityModel> > m_courseChangeTrace;
+@end verbatim
+
+There is a one-to-one correspondence between the template parameter list in
+the declaration and the formal arguments of the callback function. Here,
+there is one template parameter, which is a @code{Ptr<const MobilityModel>}.
+This tells you that you need a function that returns void and takes a
+a @code{Ptr<const MobilityModel>}. For example,
+
+@verbatim
+ void
+ CourseChangeCallback (Ptr<const MobilityModel> model)
+ {
+ ...
+ }
+@end verbatim
+
+That's all you need if you want to @code{Config::ConnectWithoutContext}. If
+you want a context, you need to @code{Config::Connect} and use a Callback
+function that takes a string context, then the required argument.
+
+@verbatim
+ void
+ CourseChangeCallback (std::string path, Ptr<const MobilityModel> model)
+ {
+ ...
+ }
+@end verbatim
+
+If you want to ensure that your @code{CourseChangeCallback} is only visible
+in your local file, you can add the keyword @code{static} and come up with:
+
+@verbatim
+ static void
+ CourseChangeCallback (std::string path, Ptr<const MobilityModel> model)
+ {
+ ...
+ }
+@end verbatim
+
+which is exactly what we used in the @code{third.cc} example.
+
+@subsubsection The Hard Way
+
+This section is entirely optional. It is going to be a bumpy ride, especially
+for those unfamiliar with the details of templates. However, if you get through
+this, you will have a very good handle on a lot of the @code{ns-3} low level
+idioms.
+
+So, again, let's figure out what signature of callback function is required for
+the ``CourseChange'' Attribute. This is going to be painful, but you only need
+to do this once. After you get through this, you will be able to just look at
+a @code{TracedCallback} and understand it.
+
+The first thing we need to look at is the declaration of the trace source.
+Recall that this is in @code{mobility-model.h}, where we have previously
+found:
+
+@verbatim
+ TracedCallback<Ptr<const MobilityModel> > m_courseChangeTrace;
+@end verbatim
+
+This declaration is for a template. The template parameter is inside the
+angle-brackets, so we are really interested in finding out what that
+@code{TracedCallback<>} is. If you have absolutely no idea where this might
+be found, grep is your friend.
+
+We are probably going to be interested in some kind of declaration in the
+@code{ns-3} source, so first change into the @code{src} directory. Then,
+we know this declaration is going to have to be in some kind of header file,
+so just grep for it using:
+
+@verbatim
+ find . -name '*.h' | xargs grep TracedCallback
+@end verbatim
+
+You'll see 124 lines fly by (I piped this through wc to see how bad it was).
+Although that may seem like it, that's not really a lot. Just pipe the output
+through more and start scanning through it. On the first page, you will see
+some very suspiciously template-looking stuff.
+
+@verbatim
+ TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::TracedCallback ()
+ TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::ConnectWithoutContext (c ...
+ TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::Connect (const CallbackB ...
+ TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::DisconnectWithoutContext ...
+ TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::Disconnect (const Callba ...
+ TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (void) const ...
+ TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1) const ...
+ TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ...
+ TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ...
+ TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ...
+ TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ...
+ TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ...
+ TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ...
+@end verbatim
+
+It turns out that all of this comes from the header file
+@code{traced-callback.h} which sounds very promising. You can then take a
+look at @code{mobility-model.h} and see that there is a line which confirms
+this hunch:
+
+@verbatim
+ #include "ns3/traced-callback.h"
+@end verbatim
+
+Of course, you could have gone at this from the other direction and started
+by looking at the includes in @code{mobility-model.h} and noticing the
+include of @code{traced-callback.h} and inferring that this must be the file
+you want.
+
+In either case, the next step is to take a look at @code{src/core/traced-callback.h}
+in your favorite editor to see what is happening.
+
+You will see a comment at the top of the file that should be comforting:
+
+@verbatim
+ An ns3::TracedCallback has almost exactly the same API as a normal ns3::Callback but
+ instead of forwarding calls to a single function (as an ns3::Callback normally does),
+ it forwards calls to a chain of ns3::Callback.
+@end verbatim
+
+This should sound very familiar and let you know you are on the right track.
+
+Just after this comment, you will find,
+
+@verbatim
+ template<typename T1 = empty, typename T2 = empty,
+ typename T3 = empty, typename T4 = empty,
+ typename T5 = empty, typename T6 = empty,
+ typename T7 = empty, typename T8 = empty>
+ class TracedCallback
+ {
+ ...
+@end verbatim
+
+This tells you that TracedCallback is a templated class. It has eight possible
+type parameters with default values. Go back and compare this with the
+declaration you are trying to understand:
+
+@verbatim
+ TracedCallback<Ptr<const MobilityModel> > m_courseChangeTrace;
+@end verbatim
+
+The @code{typename T1} in the templated class declaration corresponds to the
+@code{Ptr<const MobilityModel>} in the declaration above. All of the other
+type parameters are left as defaults. Looking at the constructor really
+doesn't tell you much. The one place where you have seen a connection made
+between your Callback function and the tracing system is in the @code{Connect}
+and @code{ConnectWithoutContext} functions. If you scroll down, you will see
+a @code{ConnectWithoutContext} method here:
+
+@verbatim
+ template<typename T1, typename T2,
+ typename T3, typename T4,
+ typename T5, typename T6,
+ typename T7, typename T8>
+ void
+ TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::ConnectWithoutContext ...
+ {
+ Callback<void,T1,T2,T3,T4,T5,T6,T7,T8> cb;
+ cb.Assign (callback);
+ m_callbackList.push_back (cb);
+ }
+@end verbatim
+
+You are now in the belly of the beast. When the template is instantiated for
+the declaration above, the compiler will replace @code{T1} with
+@code{Ptr<const MobilityModel>}.
+
+@verbatim
+ void
+ TracedCallback<Ptr<const MobilityModel>::ConnectWithoutContext ... cb
+ {
+ Callback<void, Ptr<const MobilityModel> > cb;
+ cb.Assign (callback);
+ m_callbackList.push_back (cb);
+ }
+@end verbatim
+
+You can now see the implementation of everything we've been talking about. The
+code creates a Callback of the right type and assigns your function to it. This
+is the equivalent of the @code{pfi = MyFunction} we discussed at the start of
+this section. The code then adds the Callback to the list of Callbacks for
+this source. The only thing left is to look at the definition of Callback.
+Using the same grep trick as we used to find @code{TracedCallback}, you will be
+able to find that the file @code{./core/callback.h} is the one we need to look at.
+
+If you look down through the file, you will see a lot of probably almost
+incomprehensible template code. You will eventually come to some Doxygen for
+the Callback template class, though. Fortunately, there is some English:
+
+@verbatim
+ This class template implements the Functor Design Pattern.
+ It is used to declare the type of a Callback:
+ - the first non-optional template argument represents
+ the return type of the callback.
+ - the second optional template argument represents
+ the type of the first argument to the callback.
+ - the third optional template argument represents
+ the type of the second argument to the callback.
+ - the fourth optional template argument represents
+ the type of the third argument to the callback.
+ - the fifth optional template argument represents
+ the type of the fourth argument to the callback.
+ - the sixth optional template argument represents
+ the type of the fifth argument to the callback.
+@end verbatim
+
+We are trying to figure out what the
+
+@verbatim
+ Callback<void, Ptr<const MobilityModel> > cb;
+@end verbatim
+
+declaration means. Now we are in a position to understand that the first
+(non-optional) parameter, @code{void}, represents the return type of the
+Callback. The second (non-optional) parameter, @code{Ptr<const MobilityModel>}
+represents the first argument to the callback.
+
+The Callback in question is your function to receive the trace events. From
+this you can infer that you need a function that returns @code{void} and takes
+a @code{Ptr<const MobilityModel>}. For example,
+
+@verbatim
+ void
+ CourseChangeCallback (Ptr<const MobilityModel> model)
+ {
+ ...
+ }
+@end verbatim
+
+That's all you need if you want to @code{Config::ConnectWithoutContext}. If
+you want a context, you need to @code{Config::Connect} and use a Callback
+function that takes a string context. This is because the @code{Connect}
+function will provide the context for you. You'll need:
+
+@verbatim
+ void
+ CourseChangeCallback (std::string path, Ptr<const MobilityModel> model)
+ {
+ ...
+ }
+@end verbatim
+
+If you want to ensure that your @code{CourseChangeCallback} is only visible
+in your local file, you can add the keyword @code{static} and come up with:
+
+@verbatim
+ static void
+ CourseChangeCallback (std::string path, Ptr<const MobilityModel> model)
+ {
+ ...
+ }
+@end verbatim
+
+which is exactly what we used in the @code{third.cc} example. Perhaps you
+should now go back and reread the previous section (Take My Word for It).
+
+If you are interested in more details regarding the implementation of
+Callbacks, feel free to take a look at the @code{ns-3} manual. They are one
+of the most frequently used constructs in the low-level parts of @code{ns-3}.
+It is, in my opinion, a quite elegant thing.
+
+
+
+
+
+
+
+
--- a/wscript Fri Oct 09 19:31:39 2009 +0200
+++ b/wscript Fri Oct 09 19:44:13 2009 +0200
@@ -137,16 +137,6 @@
action="store_true", default=False,
dest='lcov_report')
- opt.add_option('--doxygen',
- help=('Run doxygen to generate html documentation from source comments'),
- action="store_true", default=False,
- dest='doxygen')
- opt.add_option('--doxygen-no-build',
- help=('Run doxygen to generate html documentation from source comments, '
- 'but do not wait for ns-3 to finish the full build.'),
- action="store_true", default=False,
- dest='doxygen_no_build')
-
opt.add_option('--run',
help=('Run a locally built program; argument can be a program name,'
' or a command starting with the program name.'),
@@ -590,25 +580,12 @@
raise Utils.WafError("Cannot run regression tests: building the ns-3 examples is not enabled"
" (regression tests are based on examples)")
-# if Options.options.check:
-# Options.options.compile_targets += ',run-tests'
-# if env['ENABLE_PYTHON_BINDINGS']:
-# Options.options.compile_targets += ',ns3module,pybindgen-command'
-# _run_check(bld)
-
- if Options.options.doxygen_no_build:
- doxygen()
- raise SystemExit(0)
-
def shutdown(ctx):
bld = wutils.bld
if wutils.bld is None:
return
env = bld.env
- #if Options.commands['check']:
- # _run_waf_check()
-
if Options.options.lcov_report:
lcov_report()
@@ -626,25 +603,14 @@
if Options.options.check:
raise Utils.WafError("Please run `./test.py' now, instead of `./waf --check'")
- if Options.options.doxygen:
- doxygen()
- raise SystemExit(0)
-
check_shell(bld)
- if Options.options.doxygen:
- doxygen()
- raise SystemExit(0)
-
-
check_context = Build.BuildContext
def check(bld):
"""run the equivalent of the old ns-3 unit tests using test.py"""
- bld = wutils.bld
- env = bld.env
- wutils.run_python_program("test.py -c core", env)
-
+ env = wutils.bld.env
+ wutils.run_python_program("test.py -n -c core", env)
class print_introspected_doxygen_task(Task.TaskBase):
after = 'cc cxx cc_link cxx_link'
@@ -696,111 +662,6 @@
wutils.run_argv([self.bld.env['PYTHON'], os.path.join("..", "utils", "python-unit-tests.py")],
self.bld.env, proc_env, force_no_valgrind=True)
-#class run_a_unit_test_task(Task.TaskBase):
-# after = 'cc cxx cc_link cxx_link'
-# color = 'BLUE'
-#
-# def __init__(self, bld, name_of_test):
-# self.bld = bld
-# super(run_a_unit_test_task, self).__init__(generator=self)
-# self.name_of_test = name_of_test
-# try:
-# program_obj = wutils.find_program("run-tests", self.bld.env)
-# except ValueError, ex:
-# raise Utils.WafError(str(ex))
-# program_node = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj))
-# self.program_path = program_node.abspath(self.bld.env)
-#
-# def __str__(self):
-# return 'run-unit-test(%s)\n' % self.name_of_test
-#
-# def runnable_status(self):
-# return Task.RUN_ME
-#
-# def run(self):
-# #print repr([self.program_path, self.name_of_test])
-# try:
-# self.retval = wutils.run_argv([self.program_path, self.name_of_test], self.bld.env)
-# except Utils.WafError:
-# self.retval = 1
-# #print "running test %s: exit with %i" % (self.name_of_test, retval)
-# return 0
-#
-#class get_list_of_unit_tests_task(Task.TaskBase):
-# after = 'cc cxx cc_link cxx_link'
-# color = 'BLUE'
-#
-# def __init__(self, bld):
-# self.bld = bld
-# super(get_list_of_unit_tests_task, self).__init__(generator=self)
-# self.tests = []
-#
-# def __str__(self):
-# return 'get-unit-tests-list\n'
-#
-# def runnable_status(self):
-# return Task.RUN_ME
-#
-# def run(self):
-# try:
-# program_obj = wutils.find_program("run-tests", self.bld.env)
-# except ValueError, ex:
-# raise Utils.WafError(str(ex))
-# program_node = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj))
-# program_path = program_node.abspath(self.bld.env)
-# proc = subprocess.Popen([program_path, "--ListTests"], stdout=subprocess.PIPE,
-# env=wutils.get_proc_env())
-# self.tests = [l.rstrip() for l in proc.stdout.readlines()]
-# retval = proc.wait()
-# if retval:
-# return retval
-# test_tasks = []
-# for name_of_test in self.tests:
-# test_tasks.append(run_a_unit_test_task(self.bld, name_of_test))
-# collector = collect_unit_test_results_task(self.bld, list(test_tasks))
-# collector.run_after = list(test_tasks)
-# self.more_tasks = [collector] + test_tasks
-#
-#
-#class collect_unit_test_results_task(Task.TaskBase):
-# after = 'run_a_unit_test_task'
-# color = 'BLUE'
-#
-# def __init__(self, bld, test_tasks):
-# self.bld = bld
-# super(collect_unit_test_results_task, self).__init__(generator=self)
-# self.test_tasks = test_tasks
-#
-# def __str__(self):
-# return 'collect-unit-tests-results\n'
-#
-# def runnable_status(self):
-# for t in self.run_after:
-# if not t.hasrun:
-# return Task.ASK_LATER
-# return Task.RUN_ME
-#
-# def run(self):
-# failed_tasks = []
-# for task in self.test_tasks:
-# if task.retval:
-# failed_tasks.append(task)
-# if failed_tasks:
-# print "C++ UNIT TESTS: %i tests passed, %i failed (%s)." % \
-# (len(self.test_tasks) - len(failed_tasks), len(failed_tasks),
-# ', '.join(t.name_of_test for t in failed_tasks))
-# return 1
-# else:
-# print "C++ UNIT TESTS: all %i tests passed." % (len(self.test_tasks),)
-# return 0
-#
-#
-#def _run_check(bld):
-# task = get_list_of_unit_tests_task(bld)
-# print_introspected_doxygen_task(bld)
-# if bld.env['ENABLE_PYTHON_BINDINGS']:
-# run_python_unit_tests_task(bld)
-
def check_shell(bld):
if 'NS3_MODULE_PATH' not in os.environ:
return
@@ -835,11 +696,26 @@
env = wutils.bld.env
wutils.run_argv([shell], env, {'NS3_MODULE_PATH': os.pathsep.join(env['NS3_MODULE_PATH'])})
-def doxygen():
- if not os.path.exists('doc/introspected-doxygen.h'):
- Logs.warn("doc/introspected-doxygen.h does not exist; run waf check to generate it.")
+def doxygen(bld):
+ """do a full build, generate the introspected doxygen and then the doxygen"""
+ Scripting.build(bld)
+ env = wutils.bld.env
+ proc_env = wutils.get_proc_env()
- ## run doxygen
+ try:
+ program_obj = wutils.find_program('print-introspected-doxygen', env)
+ except ValueError:
+ Logs.warn("print-introspected-doxygen does not exist")
+ raise SystemExit(1)
+ return
+
+ prog = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj)).abspath(env)
+ out = open(os.path.join('doc', 'introspected-doxygen.h'), 'w')
+
+ if subprocess.Popen([prog], stdout=out, env=proc_env).wait():
+ raise SystemExit(1)
+ out.close()
+
doxygen_config = os.path.join('doc', 'doxygen.conf')
if subprocess.Popen(['doxygen', doxygen_config]).wait():
raise SystemExit(1)
@@ -876,9 +752,6 @@
finally:
os.chdir("..")
-
-
-
##
## The default WAF DistDir implementation is rather slow, because it
## first copies everything and only later removes unwanted files and