SENFSCons.StandardTargets(env)
SENFSCons.GlobalTargets(env)
SENFSCons.Doxygen(env)
-SENFSCons.DoxyXRef(env, env.Alias('all_docs')[0].sources,
+SENFSCons.DoxyXRef(env,
HTML_HEADER = '#/doclib/doxy-header-overview.html',
HTML_FOOTER = '#/doclib/doxy-footer.html')
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-// Definition of non-inline non-template functions
+/** \file
+ \brief senf::FileHandle non-inline non-template implementation
+ */
#include "FileHandle.hh"
//#include "FileHandle.ih"
if (::fcntl(fd(), F_SETFL, flags) < 0) throw SystemException(errno);
}
+/* We don't take POLLIN/POLLOUT as argument to avoid having to include
+ sys/poll.h in the .cci file (and therefore indirectly into the .hh
+ and then every file which uses FileHandle) */
prefix_ bool senf::FileBody::pollCheck(int fd, bool incoming, bool block)
const
{
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-// Definition of inline non-template functions
+/** \file
+ \brief senf::FileHandle inline non-template implementation
+ */
//#include "FileHandle.ih"
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+/** \file
+ \brief senf::FileHandle public header
+ */
+
#ifndef HH_FileHandle_
#define HH_FileHandle_ 1
bool eof() const; ///< Check EOF condition
/**< Depending on the socket type, this might never return \p
- true */
+ true.
+
+ This member is somewhat problematic performance wise if
+ called frequently since it relies on virtual
+ functions. However, since the eof() handling is extremely
+ protocol dependent, a policy based implementation does not
+ seam feasible. */
bool valid() const; ///< Check filehandle validity
/**< Any operation besides valid() will fail on an invalid
FileHandle */
bool boolean_test() const; ///< Short for valid() && ! eof()
/**< This is called when using a FileHandle instance in a boolen
- context */
+ context
+
+ See the performance comments for the eof() member */
int fd() const; ///< Return the raw FileHandle
- static FileHandle cast_static(FileHandle handle); ///< \internal
- static FileHandle cast_dynamic(FileHandle handle); ///< \internal
+ static FileHandle cast_static(FileHandle handle); /**< \internal */
+ static FileHandle cast_dynamic(FileHandle handle); /**< \internal */
protected:
explicit FileHandle(std::auto_ptr<FileBody> body);
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+/** \file
+ \brief senf::FileHandle internal header
+ */
+
#ifndef IH_FileHandle_
#define IH_FileHandle_ 1
namespace senf {
- /** \brief
+ /** \brief senf::FileHandle referenced body
+
+ \internal
+
+ The senf::FileBody class formes the body part of the handle/body structure of the FileHandle
+ interface. It manages the FileHandle data and is referenced by senf::FileHandle. It is
+ automatically managed using reference counting.
+
+ Since the senf::FileHandle class forwards most calls directly to the underlying
+ senf::FileBody instance, most members are documented in senf::FileHandle.
+
+ \section filebody_new Writing senf::FileBody derived classes
+
+ It is possible to write customized senf::FileBody derived body implementations. This
+ implementation can then be used be a senf::FileHandle derived class to customize the
+ FileHandle behavior. Handling the body directly by the handle class ensures, that no invalid
+ handles can be created (a senf::FileHandle derived handle expecting a specific body type but
+ pointing to a different body type).
+
+ To customize the behavior, a virtual interface is provided. This interface only covers some
+ basic funcionality which is only used infrequently during the lifetime of a FileHandle
+ instance.
+
*/
class FileBody
: public senf::intrusive_refcount
///\name Structors and default members
///@{
- explicit FileBody(int fd=-1);
+ explicit FileBody(int fd=-1); ///< Create new instance
+ /**< You need to pass a real file descriptor to this
+ constructor not some arbitrary id even if you overload
+ all the virtual members. If the file descriptor is -1 the
+ resulting body/handle is not valid() */
virtual ~FileBody();
// no copy
///////////////////////////////////////////////////////////////////////////
// Virtual interface for subclasses to override
- virtual void v_close();
- virtual void v_terminate();
- virtual bool v_eof() const;
- virtual bool v_valid() const;
-
+ virtual void v_close(); ///< Called to close the file descriptor
+ /**< You should probably always call the global ::close()
+ function in this member, however you might want to do
+ some additional cleanup here. If the operation fails, you
+ are allowed to throw (preferably a
+ senf::SystemException).
+
+ \throws senf::SystemException */
+ virtual void v_terminate(); ///< Called to forcibly close the file descriptor
+ /**< This member is called by the destructor (and by
+ terminate()) to close the descriptor. This member must \e
+ never throw, it should probably just ignore error
+ conditions (there's not much else you can do) */
+ virtual bool v_eof() const; ///< Called by eof()
+ virtual bool v_valid() const; ///< Called by valid()
+ /**< This member is only called, if the file descriptor is
+ not -1 */
protected:
// Local Variables:
// mode: c++
// c-file-style: "senf"
+// fill-column: 100
// End:
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-// Definition of non-inline non-template functions
+/** \file
+ \brief senf::SocketHandle non-inline non-template implementation
+ */
#include "SocketHandle.hh"
#include "SocketHandle.ih"
}
}
-prefix_ bool senf::detail::StateMapOrdering::operator()(std::string a1, std::string a2)
+prefix_ bool senf::detail::StateMapOrdering::operator()(std::string const & a1,
+ std::string const & a2)
const
{
std::string::iterator i1 (a1.begin());
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-// Definition of inline non-template functions
+/** \file
+ \brief senf::SocketHandle inline non-template implementation
+ */
#include "SocketHandle.ih"
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-// Definition of non-inline template functions
+/** \file
+ \brief senf::SocketHandle non-inline template implementation
+ */
#include "SocketHandle.ih"
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-// Definition of inline template functions
+/** \file
+ \brief senf::SocketHandle inline template implementation
+ */
#include "SocketHandle.ih"
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+/** \file
+ \brief senf::SocketHandle public header
+ */
+
#ifndef HH_SocketHandle_
#define HH_SocketHandle_ 1
namespace senf {
- /** \brief
+ /** \brief basic SocketHandle supporting protocol and policy abstraction
+
+ The senf::SocketHandle class introduces the two abstraction layers of the socket
+ library. senf::SocketHandle does \e not provide socket functions it only provides the
+ infrastructure necessary to support both, the protocol and the policy interface.
+
+ senf::SocketHandle takes the socket policy as a template argument. senf::SocketHandle also
+ introduces the protocol class. However, the class has no public constructors (see the
+ derived classes senf::ProtocolClientSocketHandle and senf::ProtocolServerSocketHandle).
+
+ The most important functionality provided by senf::SocketHandle is the conversion
+ constructor. This allows to implicitly convert between compatible socket handle types as
+ specified by the socket policy. The conversion constructor is defined in such a way, that
+ only valid conversions are possible (see the implementation source for a more complete
+ discussion).
- \todo Create a SocketHandleBase class and move some non-Policy
- dependent code there
+ \note This class is \e not meant to be used as a base-class outside the library
+ implementation; The protected interface is for internal use only.
+
+ \todo Create a SocketHandleBase class and move some non-Policy dependent code there
*/
template <class SocketPolicy>
class SocketHandle
typedef SocketPolicy Policy;
+ /** \brief Check policy compatibility
+
+ IsCompatible is a template meta-function which will check some other socket policy for
+ conversion compatibility. This check is used in the senf::SocketPolicy implementation to
+ restrict the conversion operator.
+ */
template <class OtherPolicy>
struct IsCompatible
: public boost::enable_if< SocketPolicyIsBaseOf<SocketPolicy,OtherPolicy>,
// default destructor
// conversion constructors
+
template <class OtherPolicy>
SocketHandle(SocketHandle<OtherPolicy> other,
typename IsCompatible<OtherPolicy>::type * = 0);
+ ///< Convert from other socket handle checking policy
+ ///< compatibility
///@}
///////////////////////////////////////////////////////////////////////////
template <class OtherPolicy>
typename IsCompatible<OtherPolicy>::type const & operator=(SocketHandle<OtherPolicy> other);
-
- static SocketHandle cast_static(FileHandle handle);
- static SocketHandle cast_dynamic(FileHandle handle);
+ ///< Assign from other socket handle checking policy
+ ///< compatibility
void state(SocketStateMap & map, unsigned lod=0);
+ ///< Inquire state information of socket handle
+ /**< The map argument (a string to string mapping) will be
+ filled with information coverning the current state of
+ the socket. The information provided depends on the
+ socket protocol. The amount of information returned can
+ be controlled using the \p lod value.
+
+ See senf::SocketProtocol::state() for more information,
+ how the Information is generated.
+
+ \param map string to string mapping to be filled with
+ state information
+ \param lod level of detail requesten. The interpretation
+ of this value is protocol specific */
std::string dumpState(unsigned lod=0);
+ ///< Format complete state information as string
+ /**< Formats the complete state map value and returns it as
+ a single multi-line string. */
protected:
explicit SocketHandle(std::auto_ptr<SocketProtocol> protocol, bool isServer);
+ ///< Initialize SocketHandle providing the protocol
+ /**< \param protocol Protocol class of the protocol
+ implemented by this socket handle
+ \param isServer \c true, if this SocketHandle instance
+ implements a server handle, \c false otherwise */
SocketHandle(FileHandle other, bool isChecked);
-
- SocketBody & body();
- SocketBody const & body() const;
+ ///< Initialize SocketHandle from arbitrary checked
+ ///< FileHandle
+ /**< This constructor is used to support up- and downcasting
+ of SocketHandle instances.
+
+ \warning It is absolutely necessary to ensure, that the
+ FileHandle passed in is \e really a SocketHandle holding
+ a SocketBody (and not a simple FileBody)
+ instance. Additionally. the SocketPolicy absolutely must
+ be compatible.
+
+ \param other FileHandle to assign
+ \param isChecked has to be \c true
+
+ \todo Answer, why the heck I need the \c isChecked
+ parameter ??
+ */
+
+ SocketBody & body(); ///< Access socket body
+ /**< This member replaces the corresponding FileHandle
+ member and returns an appropriately cast body reference */
+ SocketBody const & body() const; ///< Access socket body in const context
+ /**< This member replaces the corresponding FileHandle
+ member and returns an appropriately cast body reference */
SocketProtocol const & protocol() const;
+ ///< Access protocol class
+
+ void assign(FileHandle other); /**< \internal */
- void assign(FileHandle other);
+ public:
+ static SocketHandle cast_static(FileHandle handle);
+ static SocketHandle cast_dynamic(FileHandle handle);
private:
};
+ /** \brief Write stream status dump to output stream
+
+ Write senf::SocketHandle::dumpState() to \c os
+
+ \related senf::SocketHandle
+ */
template <class Policy>
std::ostream & operator<<(std::ostream & os, SocketHandle<Policy> handle);
+ /** \brief static socket (down-)cast
+
+ This function is like \c static_cast but for socket handles. It allows to downcast any
+ FileHandle to any SocketHandle (and its derived types). static_socket_cast will \e not check
+ the validity of the cast, it will assume, that the cast is valid.
+
+ The function will however check, that the cast is possible. Casting between (at compile
+ time) known incompatible types (like casting a SocketHandle with a communication policy of
+ ConnectedCommunicationPolicy to a SocketHandle with UnconnectedCommunicationPolicy will fail
+ at compile time).
+
+ \warning
+ If the type you cast to is not really a compatible socket handle type you will get undefined
+ behavior, probably your program will crash (You will get an assertion in debug builds).
+
+ \related senf::SocketHandle
+ */
template <class Target, class Source>
Target static_socket_cast(Source handle);
+ /** \brief dynamic socket (down-)cast
+
+ This function is like \c dynamic_cast but for socket handles. It is a runtime typechecked
+ version of static_socket_cast.
+
+ \throws std::bad_cast You have tried to perform an invalid down- or crosscast.
+
+ \related senf::SocketHandle
+ */
template <class Target, class Source>
Target dynamic_socket_cast(Source handle);
+ /** \brief dynamically check cast validity
+
+ This function will check, wether the given cast is valid. This is the same as checking, that
+ dynamic_socket_cast does not throw.
+
+ This member is needed, since there is no 'null' SocketHandle (comparable to a null pointer)
+ which could be returned by a non-throwing variant of dynamic_socket_cast.
+
+ \related senf::SocketHandle
+ */
template <class Target, class Source>
bool check_socket_cast(Source handle);
}
// Local Variables:
// mode: c++
// c-file-style: "senf"
+// fill-column: 100
// End:
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+/** \file
+ \brief senf::SocketHandle internal header
+ */
+
#ifndef IH_SocketHandle_
#define IH_SocketHandle_ 1
namespace detail {
+ /** \brief String supporting automatic type conversion
+
+ The ConvertibleString class is used to simplify creating a text representation of
+ arbitrary values. ConvertibleString is an ordinary string with an additional constructor
+ which allows constructing the string from any arbitrary, streamable type.
+
+ \note It is generally not advisable to derive from the standard library container
+ classes. However, in this concrete case, the derivation is safe since only the
+ additional functionality is added. It is absolutely safe to convert the derived class
+ back to the base type.
+ */
class ConvertibleString : public std::string
{
public:
ConvertibleString();
- ConvertibleString(bool v);
+ ConvertibleString(bool v); ///< Bool conversion constructor
+ /**< The bool conversion is defined explicitly to use a
+ specialized representation (the strings 'true' and
+ 'false') */
template <class T>
ConvertibleString(T const & other);
- template <class T>
- ConvertibleString & operator+= (ConvertibleString const & other);
- };
-
- struct StateMapOrdering
- : public std::binary_function<std::string,std::string,bool>
- {
- bool operator()(std::string a1, std::string a2) const;
- };
-
+ ///< Conversion constructor
+ /**< This constructor will assign the string from any
+ arbitrary type. It will use boost::lexical_cast to
+ convert the argument to its string representation. */
+
+ template <class T>
+ ConvertibleString & operator+= (ConvertibleString const & other);
+ ///< Add additional values with separator
+ /**< This operator facilitates the representation of
+ multiple values in a single string. Each value is first
+ converted to a string (using the type conversion
+ machinery of C++ and the ConvertibleString conversion
+ constructors). It is then appended to the current string
+ with ', ' as a separator (if the current string is
+ non-empty). */
+ };
+
+ /** \brief Special ordering for the SocketStateMap
+ \internal
+
+ This special ordering will sort 'hierarchical' strings correctly. A hierarchical string
+ in this context is a string like a path- or hostname with '.' as the hierarchical
+ separator.
+ */
+ struct StateMapOrdering
+ : public std::binary_function<std::string,std::string,bool>
+ {
+ bool operator()(std::string const & a1, std::string const & a2) const;
+ };
+
}
typedef std::map< std::string, detail::ConvertibleString, detail::StateMapOrdering > SocketStateMap;
namespace detail {
- std::string dumpState(SocketStateMap const & map);
+ /** \brief Helper to convert SocketStateMap to multiline string representation
+ \internal
+ */
+ std::string dumpState(SocketStateMap const & map);
}
+ /** \brief senf::SocketHandle referenced body
+
+ \internal
+
+ senf::SocketBody is the extended (relatively to senf::FileBody) body of
+ senf::SocketHandle. Every SocketHandle must have a SocketBody as it's body (and not a simple
+ FileBody). The casting and conversion operators defined will ensure this if used
+ properly. If this invariant is violated, your Program will probably crash.
+ */
class SocketBody
- : public FileBody
+ : public FileBody
{
public:
- ///////////////////////////////////////////////////////////////////////////
- // Types
-
- typedef boost::intrusive_ptr<SocketBody> ptr;
-
- ///////////////////////////////////////////////////////////////////////////
- ///\name Structors and default members
- ///@{
-
- explicit SocketBody(std::auto_ptr<SocketProtocol> protocol, bool isServer);
- SocketBody(std::auto_ptr<SocketProtocol> protocol, bool isServer, int fd);
-
- // no copy
- // no conversion constructors
-
- ///@}
- ///////////////////////////////////////////////////////////////////////////
-
- SocketProtocol const & protocol() const;
- bool isServer();
-
- void state(SocketStateMap & map, unsigned lod);
+ ///////////////////////////////////////////////////////////////////////////
+ // Types
+
+ typedef boost::intrusive_ptr<SocketBody> ptr;
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///\name Structors and default members
+ ///@{
+
+ SocketBody(std::auto_ptr<SocketProtocol> protocol, bool isServer);
+ /**<
+ \param protocol Protocol class implementing the desired
+ protocol
+ \param isServer \c true, if this socket is a server
+ socket, false otherwise */
+ SocketBody(std::auto_ptr<SocketProtocol> protocol, bool isServer, int fd);
+ /**<
+ \param protocol Protocol class implementing the desired
+ protocol
+ \param isServer \c true, if this socket is a server
+ socket, false otherwise
+ \param fd socket file descriptor */
+
+ // no copy
+ // no conversion constructors
+
+ ///@}
+ ///////////////////////////////////////////////////////////////////////////
+
+ SocketProtocol const & protocol() const;
+ ///< Access the protocol instance
+ bool isServer(); ///< Check socket type
+ /**< \return \c true, if this is a server socket, \c false
+ otherwise */
+
+ void state(SocketStateMap & map, unsigned lod);
+ /**< \todo Move state into a virtual body member (which is
+ ok, since SocketBody already has virtual members). This
+ makes in unnecessary to reimplement dumpState and state
+ in every SocketHandle derived class */
private:
- virtual void v_close();
- virtual void v_terminate();
- virtual bool v_eof() const;
-
- boost::scoped_ptr<SocketProtocol> protocol_;
- bool isServer_;
+ virtual void v_close(); ///< Close socket
+ /**< This override will automatically \c shutdown() the
+ socket whenever it is closed.
+ \throws senf::SystemException */
+ virtual void v_terminate(); ///< Forcibly close socket
+ /**< This override will automatically \c shutfown() the
+ socket whenever it is called. Additionally it will
+ disable SO_LINGER to ensure, that v_terminate will not
+ block. Like the overriden method, this member will ignore
+ failures and will never throw. It therefore safe to be
+ called from a destructor. */
+ virtual bool v_eof() const; ///< Check for eof condition
+ /**< Since the eof check for sockets is very protocol
+ dependent, this member will forward the call to
+ senf::SocketPolicy::eof() */
+
+ boost::scoped_ptr<SocketProtocol> protocol_;
+ bool isServer_;
};
}
// Local Variables:
// mode: c++
// c-file-style: "senf"
+// fill-column: 100
// End:
background-color: #EDE497;
}
-div.nav {
- width: auto;
- background-color: white;
- border: none;
- border-bottom: 1px solid #AF9D00;
- padding: 5px 0;
- margin: 0;
-}
-
-div.qindex {
- width: auto;
- background-color: #e8eef2;
- border: 1px solid #84b0c7;
- text-align: center;
- margin: 2px;
- padding: 2px;
- line-height: 140%;
-}
-
#footer {
clear: both;
padding: 4px 10px 4px 142px;
font-weight: bold;
}
-dl.bug {
+dl.bug, dl.fixme, dl.todo, dl.idea {
border: 1px solid #EE0000;
border-left-width: 4px;
background-color: #FFDDDD;
padding: 0 10px;
}
-dl:contains("Bug:") {
+dl:contains("Bug:"), dl:contains("Fix:"), dl:contains("Todo:"), dl:contains("Idea:") {
border: 1px solid #CC8888;
- padding: 5px;
+ padding: 4px;
background-color: #FFEEEE;
color: #666666;
+ font-size: 6px;
+ line-height: 6px;
+ overflow: hidden;
+ height: 6px;
+}
+
+dl:contains("Bug:"):hover, dl:contains("Fix:"):hover,
+dl:contains("Todo:"):hover, dl:contains("Idea:"):hover {
+ line-height: inherit;
+ font-size: inherit;
+ height: auto;
}
-dl:contains("Bug:") a {
+dl:contains("Bug:") a, dl:contains("Fix:") a, dl:contains("Todo:") a, dl:contains("Idea:") a {
color: #6666FF;
}
dl.fixme {
- border: 1px solid #EEEE00;
- border-left-width: 4px;
+ border-color: #EEEE00;
background-color: #FFFFDD;
- padding: 0 10px;
}
dl:contains("Fix:") {
- border: 1px solid #CCCC88;
- padding: 5px;
+ border-color: #CCCC88;
background-color: #FFFFEE;
- color: #666666;
-}
-
-dl:contains("Fix:") a {
- color: #6666FF;
}
dl.todo {
- border: 1px solid #00AA00;
- border-left-width: 4px;
+ border-color: #00AA00;
background-color: #DDFFDD;
- padding: 0 10px;
}
dl:contains("Todo:") {
- border: 1px solid #88CC88;
- padding: 5px;
+ border-color: #88CC88;
background-color: #EEFFEE;
- color: #666666;
-}
-
-dl:contains("Todo:") a {
- color: #6666FF;
}
dl.idea {
- border: 1px solid #AAAAAA;
- border-left-width: 4px;
+ border-color: #AAAAAA;
background-color: #EEEEEE;
- padding: 0 10px;
}
dl:contains("Idea:") {
- border: 1px solid #CCCCCC;
- padding: 5px;
+ border-color: #CCCCCC;
background-color: #F8F8F8;
- color: #666666;
}
-dl:contains("Idea:") a {
- color: #6666FF;
-}
\ No newline at end of file
+table {
+ width: 100%;
+}
+
+div.ah {
+ margin-right: 10px;
+}
+
+div.nav {
+ width: auto;
+ background-color: white;
+ border: none;
+ border-bottom: 1px solid #AF9D00;
+ padding: 5px 0;
+ margin: 0;
+}
+
+div.qindex {
+ width: auto;
+ background-color: #e8eef2;
+ border: 1px solid #84b0c7;
+ text-align: center;
+ margin: 2px 0;
+ padding: 2px;
+ line-height: 140%;
+}
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+# I have been fighting 4 problems in this implementation:
+# - A Directory target will *not* call any source scanners
+# - A Directory target will interpret the directory contents as
+# sources not targets. This means, that if a command creates that
+# directory plus contents, the target will never be up-to-date
+# (since the directory contents will change with every call of
+# scons)
+# - Theres a bug in SCons which will produce an error message for
+# directory targets if dir.sources is not set explicitly
+# - the first argument to env.Clean() must be the command line target,
+# with which the scons was invoked. This does not help to add
+# aditional files or directories to be cleaned if you don't know
+# that target (it's not possible to say 'if you clean this file,
+# also clean that one' hich is, what I had expected env.Clean to
+# do).
+#
+# Together, these problems have produced several difficulties. I have
+# solved them by
+# - Adding an (empty) stamp file as a (file) target. This target will
+# cause source scanners to be invoked
+# - Adding the documentation directory as a target (so it will be
+# cleaned up which env.Clean doesn't help me to do), but *only* if
+# scons is called to with the -c option
+# - Setting dir.sources to the known source-list to silence the error
+# message whenever a directory is added as a target
+#
+# You will find all this in the DoxyEmitter
+
import os, sys, traceback
import os.path
import glob
out_dir = data["OUTPUT_DIRECTORY"]
dir = env.Dir( os.path.join(source[0].dir.abspath, out_dir) )
dir.sources = source
- targets.append(dir)
+ if env.GetOption('clean'): targets.append(dir)
else:
out_dir = '.'
# add our output locations
for (k, v) in output_formats.iteritems():
if data.get("GENERATE_" + k, v[0]).upper() == "YES":
- # Grmpf ... need to use a File object here. The problem is, that
- # Dir.scan() is implemented to just return the directory entries
- # and does *not* invoke the source-file scanners .. ARGH !!
dir = env.Dir( os.path.join(source[0].dir.abspath, out_dir, data.get(k + "_OUTPUT", v[1])) )
- # This is needed to silence the (wrong) 'Multiple ways to
- # build the same target' message
dir.sources = source
node = env.File( os.path.join(dir.abspath, k.lower()+".stamp" ) )
targets.append(node)
- targets.append(dir)
+ if env.GetOption('clean'): targets.append(dir)
if data.has_key("GENERATE_TAGFILE"):
targets.append(env.File( os.path.join(source[0].dir.abspath, data["GENERATE_TAGFILE"]) ))
"sed -e 's/\\$$title/$TITLE/g' -e 's/\\$$projectname/Overview/g' ${SOURCES[%d]} >> $TARGET"
% (HTML_HEADER and 3 or 2))
- xref = env.Command("doc/html/xref.html", sources, commands)
+ xref = env.Command("doc/html/xref.html", sources, commands,
+ TITLE = TITLE)
env.Alias('all_docs',xref)
return xref