Commit f8d70440 authored by David Dollar's avatar David Dollar

restructure node vendoring, add includes and waf

parent d994d99d
...@@ -24,11 +24,12 @@ VENDORED_NPM="$LP_DIR/vendor/npm/npm-$NPM_VERSION" ...@@ -24,11 +24,12 @@ VENDORED_NPM="$LP_DIR/vendor/npm/npm-$NPM_VERSION"
PATH="$BUILD_DIR/bin:$PATH" PATH="$BUILD_DIR/bin:$PATH"
echo "-----> Vendoring node $NODE_VERSION" echo "-----> Vendoring node $NODE_VERSION"
mkdir -p "$BUILD_DIR/bin" mkdir -p "$BUILD_DIR/bin"
cp "$VENDORED_NODE" "$BUILD_DIR/bin/node" cp "$VENDORED_NODE/node" "$BUILD_DIR/bin/node"
# setting up paths for building # setting up paths for building
PATH="$LP_DIR/vendor/scons/scons-${SCONS_VERSION}:$PATH" PATH="$LP_DIR/vendor/scons/scons-${SCONS_VERSION}:$PATH"
PATH="$VENDORED_NODE:$PATH" PATH="$VENDORED_NODE:$PATH"
export INCLUDE_PATH="$VENDORED_NODE/include"
# unpack existing cache # unpack existing cache
rm -rf $CACHE_TARGET_DIR rm -rf $CACHE_TARGET_DIR
...@@ -39,7 +40,7 @@ fi ...@@ -39,7 +40,7 @@ fi
# install dependencies with npm # install dependencies with npm
echo "-----> Installing dependencies with npm $NPM_VERSION" echo "-----> Installing dependencies with npm $NPM_VERSION"
cd $BUILD_DIR cd $BUILD_DIR
HOME="$BUILD_DIR" $VENDORED_NODE $VENDORED_NPM/cli.js install 2>&1 | sed -u 's/^/ /' HOME="$BUILD_DIR" $VENDORED_NODE/node $VENDORED_NPM/cli.js install 2>&1 | sed -u 's/^/ /'
if [ "${PIPESTATUS[*]}" != "0 0" ]; then if [ "${PIPESTATUS[*]}" != "0 0" ]; then
echo " ! Failed to install dependencies with npm" echo " ! Failed to install dependencies with npm"
exit 1 exit 1
......
/* Configuration header created by Waf - do not edit */
#ifndef _CONFIG_H_WAF
#define _CONFIG_H_WAF
#define HAVE_OPENSSL 1
#define HAVE_PTHREAD_CREATE 1
#define HAVE_PTHREAD_ATFORK 1
#define HAVE_FUTIMES 1
/* #undef HAVE_READAHEAD */
#define HAVE_FDATASYNC 1
#define HAVE_PREADWRITE 1
#define HAVE_SENDFILE 1
/* #undef HAVE_SYNC_FILE_RANGE */
/* #undef HAVE_SYS_INOTIFY_H */
/* #undef HAVE_SYS_EPOLL_H */
/* #undef HAVE_PORT_H */
#define HAVE_POLL_H 1
#define HAVE_POLL 1
#define HAVE_SYS_EVENT_H 1
#define HAVE_SYS_QUEUE_H 1
#define HAVE_KQUEUE 1
#define HAVE_SYS_SELECT_H 1
#define HAVE_SELECT 1
/* #undef HAVE_SYS_EVENTFD_H */
/* #undef HAVE_CLOCK_SYSCALL */
#define HAVE_NANOSLEEP 1
#define HAVE_CEIL 1
#define HAVE_CONFIG_H 1
#endif /* _CONFIG_H_WAF */
/*
* libeio API header
*
* Copyright (c) 2007,2008,2009,2010 Marc Alexander Lehmann <libeio@schmorp.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License ("GPL") version 2 or any later version,
* in which case the provisions of the GPL are applicable instead of
* the above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the BSD license, indicate your decision
* by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file under
* either the BSD or the GPL.
*/
#ifndef EIO_H_
#define EIO_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <sys/types.h>
#ifdef __OpenBSD__
# include <inttypes.h>
#endif
#ifdef _WIN32
# define uid_t int
# define gid_t int
#endif
typedef struct eio_req eio_req;
typedef struct eio_dirent eio_dirent;
typedef int (*eio_cb)(eio_req *req);
#ifndef EIO_REQ_MEMBERS
# define EIO_REQ_MEMBERS
#endif
#ifndef EIO_STRUCT_STAT
# ifdef _WIN32
# define EIO_STRUCT_STAT struct _stati64
# else
# define EIO_STRUCT_STAT struct stat
# endif
#endif
#ifndef EIO_STRUCT_STATVFS
# define EIO_STRUCT_STATVFS struct statvfs
#endif
/* for readdir */
/* eio_readdir flags */
enum
{
EIO_READDIR_DENTS = 0x01, /* ptr2 contains eio_dirents, not just the (unsorted) names */
EIO_READDIR_DIRS_FIRST = 0x02, /* dirents gets sorted into a good stat() ing order to find directories first */
EIO_READDIR_STAT_ORDER = 0x04, /* dirents gets sorted into a good stat() ing order to quickly stat all files */
EIO_READDIR_FOUND_UNKNOWN = 0x80, /* set by eio_readdir when *_ARRAY was set and any TYPE=UNKNOWN's were found */
EIO_READDIR_CUSTOM1 = 0x100, /* for use by apps */
EIO_READDIR_CUSTOM2 = 0x200 /* for use by apps */
};
/* using "typical" values in the hope that the compiler will do something sensible */
enum eio_dtype
{
EIO_DT_UNKNOWN = 0,
EIO_DT_FIFO = 1,
EIO_DT_CHR = 2,
EIO_DT_MPC = 3, /* multiplexed char device (v7+coherent) */
EIO_DT_DIR = 4,
EIO_DT_NAM = 5, /* xenix special named file */
EIO_DT_BLK = 6,
EIO_DT_MPB = 7, /* multiplexed block device (v7+coherent) */
EIO_DT_REG = 8,
EIO_DT_NWK = 9, /* HP-UX network special */
EIO_DT_CMP = 9, /* VxFS compressed */
EIO_DT_LNK = 10,
/* DT_SHAD = 11,*/
EIO_DT_SOCK = 12,
EIO_DT_DOOR = 13, /* solaris door */
EIO_DT_WHT = 14,
EIO_DT_MAX = 15 /* highest DT_VALUE ever, hopefully */
};
struct eio_dirent
{
int nameofs; /* offset of null-terminated name string in (char *)req->ptr2 */
unsigned short namelen; /* size of filename without trailing 0 */
unsigned char type; /* one of EIO_DT_* */
signed char score; /* internal use */
ino_t inode; /* the inode number, if available, otherwise unspecified */
};
/* eio_msync flags */
enum
{
EIO_MS_ASYNC = 1,
EIO_MS_INVALIDATE = 2,
EIO_MS_SYNC = 4
};
/* eio_mtouch flags */
enum
{
EIO_MT_MODIFY = 1
};
/* eio_sync_file_range flags */
enum
{
EIO_SYNC_FILE_RANGE_WAIT_BEFORE = 1,
EIO_SYNC_FILE_RANGE_WRITE = 2,
EIO_SYNC_FILE_RANGE_WAIT_AFTER = 4
};
typedef double eio_tstamp; /* feel free to use double in your code directly */
/* the eio request structure */
enum
{
EIO_CUSTOM,
EIO_OPEN, EIO_CLOSE, EIO_DUP2,
EIO_READ, EIO_WRITE,
EIO_READAHEAD, EIO_SENDFILE,
EIO_STAT, EIO_LSTAT, EIO_FSTAT,
EIO_STATVFS, EIO_FSTATVFS,
EIO_TRUNCATE, EIO_FTRUNCATE,
EIO_UTIME, EIO_FUTIME,
EIO_CHMOD, EIO_FCHMOD,
EIO_CHOWN, EIO_FCHOWN,
EIO_SYNC, EIO_FSYNC, EIO_FDATASYNC,
EIO_MSYNC, EIO_MTOUCH, EIO_SYNC_FILE_RANGE,
EIO_MLOCK, EIO_MLOCKALL,
EIO_UNLINK, EIO_RMDIR, EIO_MKDIR, EIO_RENAME,
EIO_MKNOD, EIO_READDIR,
EIO_LINK, EIO_SYMLINK, EIO_READLINK,
EIO_GROUP, EIO_NOP,
EIO_BUSY
};
/* mlockall constants */
enum
{
EIO_MCL_CURRENT = 1,
EIO_MCL_FUTURE = 2,
};
/* request priorities */
enum {
EIO_PRI_MIN = -4,
EIO_PRI_MAX = 4,
EIO_PRI_DEFAULT = 0,
};
/* eio request structure */
/* this structure is mostly read-only */
/* when initialising it, all members must be zero-initialised */
struct eio_req
{
eio_req volatile *next; /* private ETP */
ssize_t result; /* result of syscall, e.g. result = read (... */
off_t offs; /* read, write, truncate, readahead, sync_file_range: file offset */
size_t size; /* read, write, readahead, sendfile, msync, mlock, sync_file_range: length */
void *ptr1; /* all applicable requests: pathname, old name; readdir: optional eio_dirents */
void *ptr2; /* all applicable requests: new name or memory buffer; readdir: name strings */
eio_tstamp nv1; /* utime, futime: atime; busy: sleep time */
eio_tstamp nv2; /* utime, futime: mtime */
int type; /* EIO_xxx constant ETP */
int int1; /* all applicable requests: file descriptor; sendfile: output fd; open, msync, mlockall, readdir: flags */
long int2; /* chown, fchown: uid; sendfile: input fd; open, chmod, mkdir, mknod: file mode, sync_file_range: flags */
long int3; /* chown, fchown: gid; mknod: dev_t */
int errorno; /* errno value on syscall return */
unsigned char flags; /* private */
signed char pri; /* the priority */
void *data;
eio_cb finish;
void (*destroy)(eio_req *req); /* called when requets no longer needed */
void (*feed)(eio_req *req); /* only used for group requests */
EIO_REQ_MEMBERS
eio_req *grp, *grp_prev, *grp_next, *grp_first; /* private */
};
/* _private_ request flags */
enum {
EIO_FLAG_CANCELLED = 0x01, /* request was cancelled */
EIO_FLAG_PTR1_FREE = 0x02, /* need to free(ptr1) */
EIO_FLAG_PTR2_FREE = 0x04, /* need to free(ptr2) */
EIO_FLAG_GROUPADD = 0x08 /* some request was added to the group */
};
/* undocumented/unsupported/private helper */
/*void eio_page_align (void **addr, size_t *length);*/
/* returns < 0 on error, errno set
* need_poll, if non-zero, will be called when results are available
* and eio_poll_cb needs to be invoked (it MUST NOT call eio_poll_cb itself).
* done_poll is called when the need to poll is gone.
*/
int eio_init (void (*want_poll)(void), void (*done_poll)(void));
/* must be called regularly to handle pending requests */
/* returns 0 if all requests were handled, -1 if not, or the value of EIO_FINISH if != 0 */
int eio_poll (void);
/* stop polling if poll took longer than duration seconds */
void eio_set_max_poll_time (eio_tstamp nseconds);
/* do not handle more then count requests in one call to eio_poll_cb */
void eio_set_max_poll_reqs (unsigned int nreqs);
/* set minimum required number
* maximum wanted number
* or maximum idle number of threads */
void eio_set_min_parallel (unsigned int nthreads);
void eio_set_max_parallel (unsigned int nthreads);
void eio_set_max_idle (unsigned int nthreads);
unsigned int eio_nreqs (void); /* number of requests in-flight */
unsigned int eio_nready (void); /* number of not-yet handled requests */
unsigned int eio_npending (void); /* numbe rof finished but unhandled requests */
unsigned int eio_nthreads (void); /* number of worker threads in use currently */
/*****************************************************************************/
/* convinience wrappers */
#ifndef EIO_NO_WRAPPERS
eio_req *eio_nop (int pri, eio_cb cb, void *data); /* does nothing except go through the whole process */
eio_req *eio_busy (eio_tstamp delay, int pri, eio_cb cb, void *data); /* ties a thread for this long, simulating busyness */
eio_req *eio_sync (int pri, eio_cb cb, void *data);
eio_req *eio_fsync (int fd, int pri, eio_cb cb, void *data);
eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data);
eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data);
eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data);
eio_req *eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data);
eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data);
eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data);
eio_req *eio_close (int fd, int pri, eio_cb cb, void *data);
eio_req *eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data);
eio_req *eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data);
eio_req *eio_write (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data);
eio_req *eio_fstat (int fd, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */
eio_req *eio_fstatvfs (int fd, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */
eio_req *eio_futime (int fd, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data);
eio_req *eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data);
eio_req *eio_fchmod (int fd, mode_t mode, int pri, eio_cb cb, void *data);
eio_req *eio_fchown (int fd, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data);
eio_req *eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data);
eio_req *eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data);
eio_req *eio_open (const char *path, int flags, mode_t mode, int pri, eio_cb cb, void *data);
eio_req *eio_utime (const char *path, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data);
eio_req *eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data);
eio_req *eio_chown (const char *path, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data);
eio_req *eio_chmod (const char *path, mode_t mode, int pri, eio_cb cb, void *data);
eio_req *eio_mkdir (const char *path, mode_t mode, int pri, eio_cb cb, void *data);
eio_req *eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */
eio_req *eio_rmdir (const char *path, int pri, eio_cb cb, void *data);
eio_req *eio_unlink (const char *path, int pri, eio_cb cb, void *data);
eio_req *eio_readlink (const char *path, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */
eio_req *eio_stat (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */
eio_req *eio_lstat (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */
eio_req *eio_statvfs (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */
eio_req *eio_mknod (const char *path, mode_t mode, dev_t dev, int pri, eio_cb cb, void *data);
eio_req *eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data);
eio_req *eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data);
eio_req *eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data);
eio_req *eio_custom (eio_cb execute, int pri, eio_cb cb, void *data);
#endif
/*****************************************************************************/
/* groups */
eio_req *eio_grp (eio_cb cb, void *data);
void eio_grp_feed (eio_req *grp, void (*feed)(eio_req *req), int limit);
void eio_grp_limit (eio_req *grp, int limit);
void eio_grp_add (eio_req *grp, eio_req *req);
void eio_grp_cancel (eio_req *grp); /* cancels all sub requests but not the group */
/*****************************************************************************/
/* request api */
/* true if the request was cancelled, useful in the invoke callback */
#define EIO_CANCELLED(req) ((req)->flags & EIO_FLAG_CANCELLED)
#define EIO_RESULT(req) ((req)->result)
/* returns a pointer to the result buffer allocated by eio */
#define EIO_BUF(req) ((req)->ptr2)
#define EIO_STAT_BUF(req) ((EIO_STRUCT_STAT *)EIO_BUF(req))
#define EIO_STATVFS_BUF(req) ((EIO_STRUCT_STATVFS *)EIO_BUF(req))
#define EIO_PATH(req) ((char *)(req)->ptr1)
/* submit a request for execution */
void eio_submit (eio_req *req);
/* cancel a request as soon fast as possible, if possible */
void eio_cancel (eio_req *req);
/* destroy a request that has never been submitted */
void eio_destroy (eio_req *req);
/*****************************************************************************/
/* convinience functions */
ssize_t eio_sendfile_sync (int ofd, int ifd, off_t offset, size_t count);
/*****************************************************************************/
/* export these so node_file can use these function instead of pread/write */
#if !HAVE_PREADWRITE
ssize_t eio__pread (int fd, void *buf, size_t count, off_t offset);
ssize_t eio__pwrite (int fd, void *buf, size_t count, off_t offset);
#endif
#ifdef __cplusplus
}
#endif
#endif
/*
* libev native API header
*
* Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann <libev@schmorp.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License ("GPL") version 2 or any later version,
* in which case the provisions of the GPL are applicable instead of
* the above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the BSD license, indicate your decision
* by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file under
* either the BSD or the GPL.
*/
#ifndef EV_H_
#define EV_H_
#ifdef __cplusplus
# define EV_CPP(x) x
#else
# define EV_CPP(x)
#endif
EV_CPP(extern "C" {)
/*****************************************************************************/
/* pre-4.0 compatibility */
#ifndef EV_COMPAT3
# define EV_COMPAT3 1
#endif
#ifndef EV_FEATURES
# define EV_FEATURES 0x7f
#endif
#define EV_FEATURE_CODE ((EV_FEATURES) & 1)
#define EV_FEATURE_DATA ((EV_FEATURES) & 2)
#define EV_FEATURE_CONFIG ((EV_FEATURES) & 4)
#define EV_FEATURE_API ((EV_FEATURES) & 8)
#define EV_FEATURE_WATCHERS ((EV_FEATURES) & 16)
#define EV_FEATURE_BACKENDS ((EV_FEATURES) & 32)
#define EV_FEATURE_OS ((EV_FEATURES) & 64)
/* these priorities are inclusive, higher priorities will be invoked earlier */
#ifndef EV_MINPRI
# define EV_MINPRI (EV_FEATURE_CONFIG ? -2 : 0)
#endif
#ifndef EV_MAXPRI
# define EV_MAXPRI (EV_FEATURE_CONFIG ? +2 : 0)
#endif
#ifndef EV_MULTIPLICITY
# define EV_MULTIPLICITY EV_FEATURE_CONFIG
#endif
#ifndef EV_PERIODIC_ENABLE
# define EV_PERIODIC_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_STAT_ENABLE
# define EV_STAT_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_PREPARE_ENABLE
# define EV_PREPARE_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_CHECK_ENABLE
# define EV_CHECK_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_IDLE_ENABLE
# define EV_IDLE_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_FORK_ENABLE
# define EV_FORK_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_CLEANUP_ENABLE
# define EV_CLEANUP_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_SIGNAL_ENABLE
# define EV_SIGNAL_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_CHILD_ENABLE
# ifdef _WIN32
# define EV_CHILD_ENABLE 0
# else
# define EV_CHILD_ENABLE EV_FEATURE_WATCHERS
#endif
#endif
#ifndef EV_ASYNC_ENABLE
# define EV_ASYNC_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_EMBED_ENABLE
# define EV_EMBED_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_WALK_ENABLE
# define EV_WALK_ENABLE 0 /* not yet */
#endif
/*****************************************************************************/
#if EV_CHILD_ENABLE && !EV_SIGNAL_ENABLE
# undef EV_SIGNAL_ENABLE
# define EV_SIGNAL_ENABLE 1
#endif
/*****************************************************************************/
typedef double ev_tstamp;
#ifndef EV_ATOMIC_T
# include <signal.h>
# define EV_ATOMIC_T sig_atomic_t volatile
#endif
#if EV_STAT_ENABLE
# ifdef _WIN32
# include <time.h>
# include <sys/types.h>
# endif
# include <sys/stat.h>
#endif
/* support multiple event loops? */
#if EV_MULTIPLICITY
struct ev_loop;
# define EV_P struct ev_loop *loop /* a loop as sole parameter in a declaration */
# define EV_P_ EV_P, /* a loop as first of multiple parameters */
# define EV_A loop /* a loop as sole argument to a function call */
# define EV_A_ EV_A, /* a loop as first of multiple arguments */
# define EV_DEFAULT_UC ev_default_loop_uc_ () /* the default loop, if initialised, as sole arg */
# define EV_DEFAULT_UC_ EV_DEFAULT_UC, /* the default loop as first of multiple arguments */
# define EV_DEFAULT ev_default_loop (0) /* the default loop as sole arg */
# define EV_DEFAULT_ EV_DEFAULT, /* the default loop as first of multiple arguments */
#else
# define EV_P void
# define EV_P_
# define EV_A
# define EV_A_
# define EV_DEFAULT
# define EV_DEFAULT_
# define EV_DEFAULT_UC
# define EV_DEFAULT_UC_
# undef EV_EMBED_ENABLE
#endif
/* EV_INLINE is used for functions in header files */
#if __STDC_VERSION__ >= 199901L || __GNUC__ >= 3
# define EV_INLINE static inline
#else
# define EV_INLINE static
#endif
/* EV_PROTOTYPES can be used to switch of prototype declarations */
#ifndef EV_PROTOTYPES
# define EV_PROTOTYPES 1
#endif
/*****************************************************************************/
#define EV_VERSION_MAJOR 4
#define EV_VERSION_MINOR 4
/* eventmask, revents, events... */
enum {
EV_UNDEF = 0xFFFFFFFF, /* guaranteed to be invalid */
EV_NONE = 0x00, /* no events */
EV_READ = 0x01, /* ev_io detected read will not block */
EV_WRITE = 0x02, /* ev_io detected write will not block */
EV__IOFDSET = 0x80, /* internal use only */
EV_IO = EV_READ, /* alias for type-detection */
EV_TIMER = 0x00000100, /* timer timed out */
#if EV_COMPAT3
EV_TIMEOUT = EV_TIMER, /* pre 4.0 API compatibility */
#endif
EV_PERIODIC = 0x00000200, /* periodic timer timed out */
EV_SIGNAL = 0x00000400, /* signal was received */
EV_CHILD = 0x00000800, /* child/pid had status change */
EV_STAT = 0x00001000, /* stat data changed */
EV_IDLE = 0x00002000, /* event loop is idling */
EV_PREPARE = 0x00004000, /* event loop about to poll */
EV_CHECK = 0x00008000, /* event loop finished poll */
EV_EMBED = 0x00010000, /* embedded event loop needs sweep */
EV_FORK = 0x00020000, /* event loop resumed in child */
EV_CLEANUP = 0x00040000, /* event loop resumed in child */
EV_ASYNC = 0x00080000, /* async intra-loop signal */
EV_CUSTOM = 0x01000000, /* for use by user code */
EV_ERROR = 0x80000000 /* sent when an error occurs */
};
/* can be used to add custom fields to all watchers, while losing binary compatibility */
#ifndef EV_COMMON
# define EV_COMMON void *data;
#endif
#ifndef EV_CB_DECLARE
# define EV_CB_DECLARE(type) void (*cb)(EV_P_ struct type *w, int revents);
#endif
#ifndef EV_CB_INVOKE
# define EV_CB_INVOKE(watcher,revents) (watcher)->cb (EV_A_ (watcher), (revents))
#endif
/* not official, do not use */
#define EV_CB(type,name) void name (EV_P_ struct ev_ ## type *w, int revents)
/*
* struct member types:
* private: you may look at them, but not change them,
* and they might not mean anything to you.
* ro: can be read anytime, but only changed when the watcher isn't active.
* rw: can be read and modified anytime, even when the watcher is active.
*
* some internal details that might be helpful for debugging:
*
* active is either 0, which means the watcher is not active,
* or the array index of the watcher (periodics, timers)
* or the array index + 1 (most other watchers)
* or simply 1 for watchers that aren't in some array.
* pending is either 0, in which case the watcher isn't,
* or the array index + 1 in the pendings array.
*/
#if EV_MINPRI == EV_MAXPRI
# define EV_DECL_PRIORITY
#elif !defined (EV_DECL_PRIORITY)
# define EV_DECL_PRIORITY int priority;
#endif
/* shared by all watchers */
#define EV_WATCHER(type) \
int active; /* private */ \
int pending; /* private */ \
EV_DECL_PRIORITY /* private */ \
EV_COMMON /* rw */ \
EV_CB_DECLARE (type) /* private */
#define EV_WATCHER_LIST(type) \
EV_WATCHER (type) \
struct ev_watcher_list *next; /* private */
#define EV_WATCHER_TIME(type) \
EV_WATCHER (type) \
ev_tstamp at; /* private */
/* base class, nothing to see here unless you subclass */
typedef struct ev_watcher
{
EV_WATCHER (ev_watcher)
} ev_watcher;
/* base class, nothing to see here unless you subclass */
typedef struct ev_watcher_list
{
EV_WATCHER_LIST (ev_watcher_list)
} ev_watcher_list;
/* base class, nothing to see here unless you subclass */
typedef struct ev_watcher_time
{
EV_WATCHER_TIME (ev_watcher_time)
} ev_watcher_time;
/* invoked when fd is either EV_READable or EV_WRITEable */
/* revent EV_READ, EV_WRITE */
typedef struct ev_io
{
EV_WATCHER_LIST (ev_io)
int fd; /* ro */
int events; /* ro */
} ev_io;
/* invoked after a specific time, repeatable (based on monotonic clock) */
/* revent EV_TIMEOUT */
typedef struct ev_timer
{
EV_WATCHER_TIME (ev_timer)
ev_tstamp repeat; /* rw */
} ev_timer;
/* invoked at some specific time, possibly repeating at regular intervals (based on UTC) */
/* revent EV_PERIODIC */
typedef struct ev_periodic
{
EV_WATCHER_TIME (ev_periodic)
ev_tstamp offset; /* rw */
ev_tstamp interval; /* rw */
ev_tstamp (*reschedule_cb)(struct ev_periodic *w, ev_tstamp now); /* rw */
} ev_periodic;
/* invoked when the given signal has been received */
/* revent EV_SIGNAL */
typedef struct ev_signal
{
EV_WATCHER_LIST (ev_signal)
int signum; /* ro */
} ev_signal;
/* invoked when sigchld is received and waitpid indicates the given pid */
/* revent EV_CHILD */
/* does not support priorities */
typedef struct ev_child
{
EV_WATCHER_LIST (ev_child)
int flags; /* private */
int pid; /* ro */
int rpid; /* rw, holds the received pid */
int rstatus; /* rw, holds the exit status, use the macros from sys/wait.h */
} ev_child;
#if EV_STAT_ENABLE
/* st_nlink = 0 means missing file or other error */
# ifdef _WIN32
typedef struct _stati64 ev_statdata;
# else
typedef struct stat ev_statdata;
# endif
/* invoked each time the stat data changes for a given path */
/* revent EV_STAT */
typedef struct ev_stat
{
EV_WATCHER_LIST (ev_stat)
ev_timer timer; /* private */
ev_tstamp interval; /* ro */
const char *path; /* ro */
ev_statdata prev; /* ro */
ev_statdata attr; /* ro */
int wd; /* wd for inotify, fd for kqueue */
} ev_stat;
#endif
#if EV_IDLE_ENABLE
/* invoked when the nothing else needs to be done, keeps the process from blocking */
/* revent EV_IDLE */
typedef struct ev_idle
{
EV_WATCHER (ev_idle)
} ev_idle;
#endif
/* invoked for each run of the mainloop, just before the blocking call */
/* you can still change events in any way you like */
/* revent EV_PREPARE */
typedef struct ev_prepare
{
EV_WATCHER (ev_prepare)
} ev_prepare;
/* invoked for each run of the mainloop, just after the blocking call */
/* revent EV_CHECK */
typedef struct ev_check
{
EV_WATCHER (ev_check)
} ev_check;
#if EV_FORK_ENABLE
/* the callback gets invoked before check in the child process when a fork was detected */
/* revent EV_FORK */
typedef struct ev_fork
{
EV_WATCHER (ev_fork)
} ev_fork;
#endif
#if EV_CLEANUP_ENABLE
/* is invoked just before the loop gets destroyed */
/* revent EV_CLEANUP */
typedef struct ev_cleanup
{
EV_WATCHER (ev_cleanup)
} ev_cleanup;
#endif
#if EV_EMBED_ENABLE
/* used to embed an event loop inside another */
/* the callback gets invoked when the event loop has handled events, and can be 0 */
typedef struct ev_embed
{
EV_WATCHER (ev_embed)
struct ev_loop *other; /* ro */
ev_io io; /* private */
ev_prepare prepare; /* private */
ev_check check; /* unused */
ev_timer timer; /* unused */
ev_periodic periodic; /* unused */
ev_idle idle; /* unused */
ev_fork fork; /* private */
#if EV_CLEANUP_ENABLE
ev_cleanup cleanup; /* unused */
#endif
} ev_embed;
#endif
#if EV_ASYNC_ENABLE
/* invoked when somebody calls ev_async_send on the watcher */
/* revent EV_ASYNC */
typedef struct ev_async
{
EV_WATCHER (ev_async)
EV_ATOMIC_T sent; /* private */
} ev_async;
# define ev_async_pending(w) (+(w)->sent)
#endif
/* the presence of this union forces similar struct layout */
union ev_any_watcher
{
struct ev_watcher w;
struct ev_watcher_list wl;
struct ev_io io;
struct ev_timer timer;
struct ev_periodic periodic;
struct ev_signal signal;
struct ev_child child;
#if EV_STAT_ENABLE
struct ev_stat stat;
#endif
#if EV_IDLE_ENABLE
struct ev_idle idle;
#endif
struct ev_prepare prepare;
struct ev_check check;
#if EV_FORK_ENABLE
struct ev_fork fork;
#endif
#if EV_CLEANUP_ENABLE
struct ev_cleanup cleanup;
#endif
#if EV_EMBED_ENABLE
struct ev_embed embed;
#endif
#if EV_ASYNC_ENABLE
struct ev_async async;
#endif
};
/* flag bits for ev_default_loop and ev_loop_new */
enum {
/* the default */
EVFLAG_AUTO = 0x00000000U, /* not quite a mask */
/* flag bits */
EVFLAG_NOENV = 0x01000000U, /* do NOT consult environment */
EVFLAG_FORKCHECK = 0x02000000U, /* check for a fork in each iteration */
/* debugging/feature disable */
EVFLAG_NOINOTIFY = 0x00100000U, /* do not attempt to use inotify */
#if EV_COMPAT3
EVFLAG_NOSIGFD = 0, /* compatibility to pre-3.9 */
#endif
EVFLAG_SIGNALFD = 0x00200000U, /* attempt to use signalfd */
EVFLAG_NOSIGMASK = 0x00400000U /* avoid modifying the signal mask */
};
/* method bits to be ored together */
enum {
EVBACKEND_SELECT = 0x00000001U, /* about anywhere */
EVBACKEND_POLL = 0x00000002U, /* !win */
EVBACKEND_EPOLL = 0x00000004U, /* linux */
EVBACKEND_KQUEUE = 0x00000008U, /* bsd */
EVBACKEND_DEVPOLL = 0x00000010U, /* solaris 8 */ /* NYI */
EVBACKEND_PORT = 0x00000020U, /* solaris 10 */
EVBACKEND_ALL = 0x0000003FU, /* all known backends */
EVBACKEND_MASK = 0x0000FFFFU /* all future backends */
};
#if EV_PROTOTYPES
int ev_version_major (void);
int ev_version_minor (void);
unsigned int ev_supported_backends (void);
unsigned int ev_recommended_backends (void);
unsigned int ev_embeddable_backends (void);
ev_tstamp ev_time (void);
void ev_sleep (ev_tstamp delay); /* sleep for a while */
/* Sets the allocation function to use, works like realloc.
* It is used to allocate and free memory.
* If it returns zero when memory needs to be allocated, the library might abort
* or take some potentially destructive action.
* The default is your system realloc function.
*/
void ev_set_allocator (void *(*cb)(void *ptr, long size));
/* set the callback function to call on a
* retryable syscall error
* (such as failed select, poll, epoll_wait)
*/
void ev_set_syserr_cb (void (*cb)(const char *msg));
#if EV_MULTIPLICITY
/* the default loop is the only one that handles signals and child watchers */
/* you can call this as often as you like */
struct ev_loop *ev_default_loop (unsigned int flags EV_CPP (= 0));
EV_INLINE struct ev_loop *
ev_default_loop_uc_ (void)
{
extern struct ev_loop *ev_default_loop_ptr;
return ev_default_loop_ptr;
}
EV_INLINE int
ev_is_default_loop (EV_P)
{
return EV_A == EV_DEFAULT_UC;
}
/* create and destroy alternative loops that don't handle signals */
struct ev_loop *ev_loop_new (unsigned int flags EV_CPP (= 0));
ev_tstamp ev_now (EV_P); /* time w.r.t. timers and the eventloop, updated after each poll */
#else
int ev_default_loop (unsigned int flags EV_CPP (= 0)); /* returns true when successful */
EV_INLINE ev_tstamp
ev_now (void)
{
extern ev_tstamp ev_rt_now;
return ev_rt_now;
}
/* looks weird, but ev_is_default_loop (EV_A) still works if this exists */
EV_INLINE int
ev_is_default_loop (void)
{
return 1;
}
#endif /* multiplicity */
/* destroy event loops, also works for the default loop */
void ev_loop_destroy (EV_P);
/* this needs to be called after fork, to duplicate the loop */
/* when you want to re-use it in the child */
/* you can call it in either the parent or the child */
/* you can actually call it at any time, anywhere :) */
void ev_loop_fork (EV_P);
unsigned int ev_backend (EV_P); /* backend in use by loop */
void ev_now_update (EV_P); /* update event loop time */
#if EV_WALK_ENABLE
/* walk (almost) all watchers in the loop of a given type, invoking the */
/* callback on every such watcher. The callback might stop the watcher, */
/* but do nothing else with the loop */
void ev_walk (EV_P_ int types, void (*cb)(EV_P_ int type, void *w));
#endif
#endif /* prototypes */
/* ev_run flags values */
enum {
EVRUN_NOWAIT = 1, /* do not block/wait */
EVRUN_ONCE = 2 /* block *once* only */
};
/* ev_break how values */
enum {
EVBREAK_CANCEL = 0, /* undo unloop */
EVBREAK_ONE = 1, /* unloop once */
EVBREAK_ALL = 2 /* unloop all loops */
};
#if EV_PROTOTYPES
void ev_run (EV_P_ int flags EV_CPP (= 0));
void ev_break (EV_P_ int how EV_CPP (= EVBREAK_ONE)); /* break out of the loop */
/*
* ref/unref can be used to add or remove a refcount on the mainloop. every watcher
* keeps one reference. if you have a long-running watcher you never unregister that
* should not keep ev_loop from running, unref() after starting, and ref() before stopping.
*/
void ev_ref (EV_P);
void ev_unref (EV_P);
/*
* convenience function, wait for a single event, without registering an event watcher
* if timeout is < 0, do wait indefinitely
*/
void ev_once (EV_P_ int fd, int events, ev_tstamp timeout, void (*cb)(int revents, void *arg), void *arg);
# if EV_FEATURE_API
unsigned int ev_iteration (EV_P); /* number of loop iterations */
unsigned int ev_depth (EV_P); /* #ev_loop enters - #ev_loop leaves */
void ev_verify (EV_P); /* abort if loop data corrupted */
void ev_set_io_collect_interval (EV_P_ ev_tstamp interval); /* sleep at least this time, default 0 */
void ev_set_timeout_collect_interval (EV_P_ ev_tstamp interval); /* sleep at least this time, default 0 */
/* advanced stuff for threading etc. support, see docs */
void ev_set_userdata (EV_P_ void *data);
void *ev_userdata (EV_P);
void ev_set_invoke_pending_cb (EV_P_ void (*invoke_pending_cb)(EV_P));
void ev_set_loop_release_cb (EV_P_ void (*release)(EV_P), void (*acquire)(EV_P));
unsigned int ev_pending_count (EV_P); /* number of pending events, if any */
void ev_invoke_pending (EV_P); /* invoke all pending watchers */
/*
* stop/start the timer handling.
*/
void ev_suspend (EV_P);
void ev_resume (EV_P);
#endif
#endif
/* these may evaluate ev multiple times, and the other arguments at most once */
/* either use ev_init + ev_TYPE_set, or the ev_TYPE_init macro, below, to first initialise a watcher */
#define ev_init(ev,cb_) do { \
((ev_watcher *)(void *)(ev))->active = \
((ev_watcher *)(void *)(ev))->pending = 0; \
ev_set_priority ((ev), 0); \
ev_set_cb ((ev), cb_); \
} while (0)
#define ev_io_set(ev,fd_,events_) do { (ev)->fd = (fd_); (ev)->events = (events_) | EV__IOFDSET; } while (0)
#define ev_timer_set(ev,after_,repeat_) do { ((ev_watcher_time *)(ev))->at = (after_); (ev)->repeat = (repeat_); } while (0)
#define ev_periodic_set(ev,ofs_,ival_,rcb_) do { (ev)->offset = (ofs_); (ev)->interval = (ival_); (ev)->reschedule_cb = (rcb_); } while (0)
#define ev_signal_set(ev,signum_) do { (ev)->signum = (signum_); } while (0)
#define ev_child_set(ev,pid_,trace_) do { (ev)->pid = (pid_); (ev)->flags = !!(trace_); } while (0)
#define ev_stat_set(ev,path_,interval_) do { (ev)->path = (path_); (ev)->interval = (interval_); (ev)->wd = -2; } while (0)
#define ev_idle_set(ev) /* nop, yes, this is a serious in-joke */
#define ev_prepare_set(ev) /* nop, yes, this is a serious in-joke */
#define ev_check_set(ev) /* nop, yes, this is a serious in-joke */
#define ev_embed_set(ev,other_) do { (ev)->other = (other_); } while (0)
#define ev_fork_set(ev) /* nop, yes, this is a serious in-joke */
#define ev_cleanup_set(ev) /* nop, yes, this is a serious in-joke */
#define ev_async_set(ev) /* nop, yes, this is a serious in-joke */
#define ev_io_init(ev,cb,fd,events) do { ev_init ((ev), (cb)); ev_io_set ((ev),(fd),(events)); } while (0)
#define ev_timer_init(ev,cb,after,repeat) do { ev_init ((ev), (cb)); ev_timer_set ((ev),(after),(repeat)); } while (0)
#define ev_periodic_init(ev,cb,ofs,ival,rcb) do { ev_init ((ev), (cb)); ev_periodic_set ((ev),(ofs),(ival),(rcb)); } while (0)
#define ev_signal_init(ev,cb,signum) do { ev_init ((ev), (cb)); ev_signal_set ((ev), (signum)); } while (0)
#define ev_child_init(ev,cb,pid,trace) do { ev_init ((ev), (cb)); ev_child_set ((ev),(pid),(trace)); } while (0)
#define ev_stat_init(ev,cb,path,interval) do { ev_init ((ev), (cb)); ev_stat_set ((ev),(path),(interval)); } while (0)
#define ev_idle_init(ev,cb) do { ev_init ((ev), (cb)); ev_idle_set ((ev)); } while (0)
#define ev_prepare_init(ev,cb) do { ev_init ((ev), (cb)); ev_prepare_set ((ev)); } while (0)
#define ev_check_init(ev,cb) do { ev_init ((ev), (cb)); ev_check_set ((ev)); } while (0)
#define ev_embed_init(ev,cb,other) do { ev_init ((ev), (cb)); ev_embed_set ((ev),(other)); } while (0)
#define ev_fork_init(ev,cb) do { ev_init ((ev), (cb)); ev_fork_set ((ev)); } while (0)
#define ev_cleanup_init(ev,cb) do { ev_init ((ev), (cb)); ev_cleanup_set ((ev)); } while (0)
#define ev_async_init(ev,cb) do { ev_init ((ev), (cb)); ev_async_set ((ev)); } while (0)
#define ev_is_pending(ev) (0 + ((ev_watcher *)(void *)(ev))->pending) /* ro, true when watcher is waiting for callback invocation */
#define ev_is_active(ev) (0 + ((ev_watcher *)(void *)(ev))->active) /* ro, true when the watcher has been started */
#define ev_cb(ev) (ev)->cb /* rw */
#if EV_MINPRI == EV_MAXPRI
# define ev_priority(ev) ((ev), EV_MINPRI)
# define ev_set_priority(ev,pri) ((ev), (pri))
#else
# define ev_priority(ev) (+(((ev_watcher *)(void *)(ev))->priority))
# define ev_set_priority(ev,pri) ( (ev_watcher *)(void *)(ev))->priority = (pri)
#endif
#define ev_periodic_at(ev) (+((ev_watcher_time *)(ev))->at)
#ifndef ev_set_cb
# define ev_set_cb(ev,cb_) ev_cb (ev) = (cb_)
#endif
/* stopping (enabling, adding) a watcher does nothing if it is already running */
/* stopping (disabling, deleting) a watcher does nothing unless its already running */
#if EV_PROTOTYPES
/* feeds an event into a watcher as if the event actually occured */
/* accepts any ev_watcher type */
void ev_feed_event (EV_P_ void *w, int revents);
void ev_feed_fd_event (EV_P_ int fd, int revents);
#if EV_SIGNAL_ENABLE
void ev_feed_signal (int signum);
void ev_feed_signal_event (EV_P_ int signum);
#endif
void ev_invoke (EV_P_ void *w, int revents);
int ev_clear_pending (EV_P_ void *w);
void ev_io_start (EV_P_ ev_io *w);
void ev_io_stop (EV_P_ ev_io *w);
void ev_timer_start (EV_P_ ev_timer *w);
void ev_timer_stop (EV_P_ ev_timer *w);
/* stops if active and no repeat, restarts if active and repeating, starts if inactive and repeating */
void ev_timer_again (EV_P_ ev_timer *w);
/* return remaining time */
ev_tstamp ev_timer_remaining (EV_P_ ev_timer *w);
#if EV_PERIODIC_ENABLE
void ev_periodic_start (EV_P_ ev_periodic *w);
void ev_periodic_stop (EV_P_ ev_periodic *w);
void ev_periodic_again (EV_P_ ev_periodic *w);
#endif
/* only supported in the default loop */
#if EV_SIGNAL_ENABLE
void ev_signal_start (EV_P_ ev_signal *w);
void ev_signal_stop (EV_P_ ev_signal *w);
#endif
/* only supported in the default loop */
# if EV_CHILD_ENABLE
void ev_child_start (EV_P_ ev_child *w);
void ev_child_stop (EV_P_ ev_child *w);
# endif
# if EV_STAT_ENABLE
void ev_stat_start (EV_P_ ev_stat *w);
void ev_stat_stop (EV_P_ ev_stat *w);
void ev_stat_stat (EV_P_ ev_stat *w);
# endif
# if EV_IDLE_ENABLE
void ev_idle_start (EV_P_ ev_idle *w);
void ev_idle_stop (EV_P_ ev_idle *w);
# endif
#if EV_PREPARE_ENABLE
void ev_prepare_start (EV_P_ ev_prepare *w);
void ev_prepare_stop (EV_P_ ev_prepare *w);
#endif
#if EV_CHECK_ENABLE
void ev_check_start (EV_P_ ev_check *w);
void ev_check_stop (EV_P_ ev_check *w);
#endif
# if EV_FORK_ENABLE
void ev_fork_start (EV_P_ ev_fork *w);
void ev_fork_stop (EV_P_ ev_fork *w);
# endif
# if EV_CLEANUP_ENABLE
void ev_cleanup_start (EV_P_ ev_cleanup *w);
void ev_cleanup_stop (EV_P_ ev_cleanup *w);
# endif
# if EV_EMBED_ENABLE
/* only supported when loop to be embedded is in fact embeddable */
void ev_embed_start (EV_P_ ev_embed *w);
void ev_embed_stop (EV_P_ ev_embed *w);
void ev_embed_sweep (EV_P_ ev_embed *w);
# endif
# if EV_ASYNC_ENABLE
void ev_async_start (EV_P_ ev_async *w);
void ev_async_stop (EV_P_ ev_async *w);
void ev_async_send (EV_P_ ev_async *w);
# endif
#if EV_COMPAT3
#define EVLOOP_NONBLOCK EVRUN_NOWAIT
#define EVLOOP_ONESHOT EVRUN_ONCE
#define EVUNLOOP_CANCEL EVBREAK_CANCEL
#define EVUNLOOP_ONE EVBREAK_ONE
#define EVUNLOOP_ALL EVBREAK_ALL
#if EV_PROTOTYPES
EV_INLINE void ev_loop (EV_P_ int flags) { ev_run (EV_A_ flags); }
EV_INLINE void ev_unloop (EV_P_ int how ) { ev_break (EV_A_ how ); }
EV_INLINE void ev_default_destroy (void) { ev_loop_destroy (EV_DEFAULT); }
EV_INLINE void ev_default_fork (void) { ev_loop_fork (EV_DEFAULT); }
#if EV_FEATURE_API
EV_INLINE unsigned int ev_loop_count (EV_P) { return ev_iteration (EV_A); }
EV_INLINE unsigned int ev_loop_depth (EV_P) { return ev_depth (EV_A); }
EV_INLINE void ev_loop_verify (EV_P) { ev_verify (EV_A); }
#endif
#endif
#else
typedef struct ev_loop ev_loop;
#endif
#endif
EV_CPP(})
#endif
// Copyright Joyent, Inc. and other Node contributors.
//
// 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.
#ifndef SRC_NODE_H_
#define SRC_NODE_H_
#include <ev.h>
#include <eio.h>
#include <v8.h>
#include <sys/types.h> /* struct stat */
#include <sys/stat.h>
#include <node_object_wrap.h>
#ifndef NODE_STRINGIFY
#define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)
#define NODE_STRINGIFY_HELPER(n) #n
#endif
namespace node {
int Start (int argc, char *argv[]);
#define NODE_PSYMBOL(s) Persistent<String>::New(String::NewSymbol(s))
/* Converts a unixtime to V8 Date */
#define NODE_UNIXTIME_V8(t) v8::Date::New(1000*static_cast<double>(t))
#define NODE_V8_UNIXTIME(v) (static_cast<double>((v)->NumberValue())/1000.0);
#define NODE_DEFINE_CONSTANT(target, constant) \
(target)->Set(v8::String::NewSymbol(#constant), \
v8::Integer::New(constant), \
static_cast<v8::PropertyAttribute>(v8::ReadOnly|v8::DontDelete))
#define NODE_SET_METHOD(obj, name, callback) \
obj->Set(v8::String::NewSymbol(name), \
v8::FunctionTemplate::New(callback)->GetFunction())
#define NODE_SET_PROTOTYPE_METHOD(templ, name, callback) \
do { \
v8::Local<v8::Signature> __callback##_SIG = v8::Signature::New(templ); \
v8::Local<v8::FunctionTemplate> __callback##_TEM = \
v8::FunctionTemplate::New(callback, v8::Handle<v8::Value>(), \
__callback##_SIG); \
templ->PrototypeTemplate()->Set(v8::String::NewSymbol(name), \
__callback##_TEM); \
} while (0)
enum encoding {ASCII, UTF8, BASE64, UCS2, BINARY};
enum encoding ParseEncoding(v8::Handle<v8::Value> encoding_v,
enum encoding _default = BINARY);
void FatalException(v8::TryCatch &try_catch);
void DisplayExceptionLine(v8::TryCatch &try_catch); // hack
v8::Local<v8::Value> Encode(const void *buf, size_t len,
enum encoding encoding = BINARY);
// Returns -1 if the handle was not valid for decoding
ssize_t DecodeBytes(v8::Handle<v8::Value>,
enum encoding encoding = BINARY);
// returns bytes written.
ssize_t DecodeWrite(char *buf,
size_t buflen,
v8::Handle<v8::Value>,
enum encoding encoding = BINARY);
// Use different stat structs & calls on windows and posix;
// on windows, _stati64 is utf-8 and big file aware.
#if __POSIX__
# define NODE_STAT stat
# define NODE_FSTAT fstat
# define NODE_STAT_STRUCT struct stat
#else // __MINGW32__
# define NODE_STAT _stati64
# define NODE_FSTAT _fstati64
# define NODE_STAT_STRUCT struct _stati64
#endif
v8::Local<v8::Object> BuildStatsObject(NODE_STAT_STRUCT *s);
/**
* Call this when your constructor is invoked as a regular function, e.g. Buffer(10) instead of new Buffer(10).
* @param constructorTemplate Constructor template to instantiate from.
* @param args The arguments object passed to your constructor.
* @see v8::Arguments::IsConstructCall
*/
v8::Handle<v8::Value> FromConstructorTemplate(v8::Persistent<v8::FunctionTemplate>& constructorTemplate, const v8::Arguments& args);
static inline v8::Persistent<v8::Function>* cb_persist(
const v8::Local<v8::Value> &v) {
v8::Persistent<v8::Function> *fn = new v8::Persistent<v8::Function>();
*fn = v8::Persistent<v8::Function>::New(v8::Local<v8::Function>::Cast(v));
return fn;
}
static inline v8::Persistent<v8::Function>* cb_unwrap(void *data) {
v8::Persistent<v8::Function> *cb =
reinterpret_cast<v8::Persistent<v8::Function>*>(data);
assert((*cb)->IsFunction());
return cb;
}
static inline void cb_destroy(v8::Persistent<v8::Function> * cb) {
cb->Dispose();
delete cb;
}
v8::Local<v8::Value> ErrnoException(int errorno,
const char *syscall = NULL,
const char *msg = "",
const char *path = NULL);
const char *signo_string(int errorno);
struct node_module_struct {
int version;
void *dso_handle;
const char *filename;
void (*register_func) (v8::Handle<v8::Object> target);
const char *modname;
};
node_module_struct* get_builtin_module(const char *name);
/**
* When this version number is changed, node.js will refuse
* to load older modules. This should be done whenever
* an API is broken in the C++ side, including in v8 or
* other dependencies
*/
#define NODE_MODULE_VERSION (1)
#define NODE_STANDARD_MODULE_STUFF \
NODE_MODULE_VERSION, \
NULL, \
__FILE__
#define NODE_MODULE(modname, regfunc) \
node::node_module_struct modname ## _module = \
{ \
NODE_STANDARD_MODULE_STUFF, \
regfunc, \
NODE_STRINGIFY(modname) \
};
#define NODE_MODULE_DECL(modname) \
extern node::node_module_struct modname ## _module;
} // namespace node
#endif // SRC_NODE_H_
// Copyright Joyent, Inc. and other Node contributors.
//
// 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.
#ifndef NODE_BUFFER_H_
#define NODE_BUFFER_H_
#include <node.h>
#include <node_object_wrap.h>
#include <v8.h>
#include <assert.h>
namespace node {
/* A buffer is a chunk of memory stored outside the V8 heap, mirrored by an
* object in javascript. The object is not totally opaque, one can access
* individual bytes with [] and slice it into substrings or sub-buffers
* without copying memory.
*
* // return an ascii encoded string - no memory is copied
* buffer.asciiSlice(0, 3)
*/
/*
The C++ API for Buffer changed radically between v0.2 and v0.3, in fact
it was the reason for bumping the version. In v0.2 JavaScript Buffers and
C++ Buffers were in one-to-one correspondence via ObjectWrap. We found
that it was faster to expose the C++ Buffers to JavaScript as a
"SlowBuffer" which is used as a private backend to pure JavaScript
"Buffer" objects - a 'Buffer' in v0.3 might look like this:
{ _parent: s,
_offset: 520,
length: 5 }
Migrating code C++ Buffer code from v0.2 to v0.3 is difficult. Here are
some tips:
- buffer->data() calls should become Buffer::Data(buffer) calls.
- buffer->length() calls should become Buffer::Length(buffer) calls.
- There should not be any ObjectWrap::Unwrap<Buffer>() calls. You should
not be storing pointers to Buffer objects at all - as they are
now considered internal structures. Instead consider making a
JavaScript reference to the buffer.
See the source code node-png as an example of a module which successfully
compiles on both v0.2 and v0.3 while making heavy use of the C++ Buffer
API.
*/
class Buffer : public ObjectWrap {
public:
static bool HasInstance(v8::Handle<v8::Value> val);
static inline char* Data(v8::Handle<v8::Object> obj) {
return (char*)obj->GetIndexedPropertiesExternalArrayData();
}
static inline char* Data(Buffer *b) {
return Buffer::Data(b->handle_);
}
static inline size_t Length(v8::Handle<v8::Object> obj) {
return (size_t)obj->GetIndexedPropertiesExternalArrayDataLength();
}
static inline size_t Length(Buffer *b) {
return Buffer::Length(b->handle_);
}
~Buffer();
typedef void (*free_callback)(char *data, void *hint);
// C++ API for constructing fast buffer
static v8::Handle<v8::Object> New(v8::Handle<v8::String> string);
static void Initialize(v8::Handle<v8::Object> target);
static Buffer* New(size_t length); // public constructor
static Buffer* New(char *data, size_t len); // public constructor
static Buffer* New(char *data, size_t length,
free_callback callback, void *hint); // public constructor
private:
static v8::Persistent<v8::FunctionTemplate> constructor_template;
static v8::Handle<v8::Value> New(const v8::Arguments &args);
static v8::Handle<v8::Value> BinarySlice(const v8::Arguments &args);
static v8::Handle<v8::Value> AsciiSlice(const v8::Arguments &args);
static v8::Handle<v8::Value> Base64Slice(const v8::Arguments &args);
static v8::Handle<v8::Value> Utf8Slice(const v8::Arguments &args);
static v8::Handle<v8::Value> Ucs2Slice(const v8::Arguments &args);
static v8::Handle<v8::Value> BinaryWrite(const v8::Arguments &args);
static v8::Handle<v8::Value> Base64Write(const v8::Arguments &args);
static v8::Handle<v8::Value> AsciiWrite(const v8::Arguments &args);
static v8::Handle<v8::Value> Utf8Write(const v8::Arguments &args);
static v8::Handle<v8::Value> Ucs2Write(const v8::Arguments &args);
static v8::Handle<v8::Value> ByteLength(const v8::Arguments &args);
static v8::Handle<v8::Value> MakeFastBuffer(const v8::Arguments &args);
static v8::Handle<v8::Value> Copy(const v8::Arguments &args);
Buffer(v8::Handle<v8::Object> wrapper, size_t length);
void Replace(char *data, size_t length, free_callback callback, void *hint);
size_t length_;
char* data_;
free_callback callback_;
void* callback_hint_;
};
} // namespace node buffer
#endif // NODE_BUFFER_H_
// Copyright Joyent, Inc. and other Node contributors.
//
// 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.
#ifndef NODE_CONFIG_H
#define NODE_CONFIG_H
#define NODE_CFLAGS "-rdynamic -D_GNU_SOURCE -DHAVE_CONFIG_H=1 -pthread -arch x86_64 -g -O3 -DHAVE_OPENSSL=1 -DEV_FORK_ENABLE=0 -DEV_EMBED_ENABLE=0 -DEV_MULTIPLICITY=0 -DX_STACKSIZE=65536 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DEV_MULTIPLICITY=0 -DHAVE_FDATASYNC=0 -DPLATFORM=\"darwin\" -D__POSIX__=1 -Wno-unused-parameter -D_FORTIFY_SOURCE=2 -DNDEBUG -I/tmp/n47/include/node"
#define NODE_PREFIX "/tmp/n47"
#endif /* NODE_CONFIG_H */
// Copyright Joyent, Inc. and other Node contributors.
//
// 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.
#ifndef SRC_EVENTS_H_
#define SRC_EVENTS_H_
#include <node_object_wrap.h>
#include <v8.h>
namespace node {
class EventEmitter : public ObjectWrap {
public:
static void Initialize(v8::Local<v8::FunctionTemplate> ctemplate);
static v8::Persistent<v8::FunctionTemplate> constructor_template;
bool Emit(v8::Handle<v8::String> event,
int argc,
v8::Handle<v8::Value> argv[]);
protected:
EventEmitter() : ObjectWrap () { }
};
} // namespace node
#endif // SRC_EVENTS_H_
// Copyright Joyent, Inc. and other Node contributors.
//
// 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.
#ifndef object_wrap_h
#define object_wrap_h
#include <v8.h>
#include <assert.h>
namespace node {
class ObjectWrap {
public:
ObjectWrap ( ) {
refs_ = 0;
}
virtual ~ObjectWrap ( ) {
if (!handle_.IsEmpty()) {
assert(handle_.IsNearDeath());
handle_.ClearWeak();
handle_->SetInternalField(0, v8::Undefined());
handle_.Dispose();
handle_.Clear();
}
}
template <class T>
static inline T* Unwrap (v8::Handle<v8::Object> handle) {
assert(!handle.IsEmpty());
assert(handle->InternalFieldCount() > 0);
return static_cast<T*>(handle->GetPointerFromInternalField(0));
}
v8::Persistent<v8::Object> handle_; // ro
protected:
inline void Wrap (v8::Handle<v8::Object> handle) {
assert(handle_.IsEmpty());
assert(handle->InternalFieldCount() > 0);
handle_ = v8::Persistent<v8::Object>::New(handle);
handle_->SetPointerInInternalField(0, this);
MakeWeak();
}
inline void MakeWeak (void) {
handle_.MakeWeak(this, WeakCallback);
}
/* Ref() marks the object as being attached to an event loop.
* Refed objects will not be garbage collected, even if
* all references are lost.
*/
virtual void Ref() {
assert(!handle_.IsEmpty());
refs_++;
handle_.ClearWeak();
}
/* Unref() marks an object as detached from the event loop. This is its
* default state. When an object with a "weak" reference changes from
* attached to detached state it will be freed. Be careful not to access
* the object after making this call as it might be gone!
* (A "weak reference" means an object that only has a
* persistant handle.)
*
* DO NOT CALL THIS FROM DESTRUCTOR
*/
virtual void Unref() {
assert(!handle_.IsEmpty());
assert(!handle_.IsWeak());
assert(refs_ > 0);
if (--refs_ == 0) { MakeWeak(); }
}
int refs_; // ro
private:
static void WeakCallback (v8::Persistent<v8::Value> value, void *data) {
ObjectWrap *obj = static_cast<ObjectWrap*>(data);
assert(value == obj->handle_);
assert(!obj->refs_);
assert(value.IsNearDeath());
delete obj;
}
};
} // namespace node
#endif // object_wrap_h
// Copyright Joyent, Inc. and other Node contributors.
//
// 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.
#include "node_config.h"
#ifndef NODE_VERSION_H
#define NODE_VERSION_H
#define NODE_MAJOR_VERSION 0
#define NODE_MINOR_VERSION 4
#define NODE_PATCH_VERSION 7
#define NODE_VERSION_IS_RELEASE 1
#ifndef NODE_STRINGIFY
#define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)
#define NODE_STRINGIFY_HELPER(n) #n
#endif
#if NODE_VERSION_IS_RELEASE
# define NODE_VERSION_STRING NODE_STRINGIFY(NODE_MAJOR_VERSION) "." \
NODE_STRINGIFY(NODE_MINOR_VERSION) "." \
NODE_STRINGIFY(NODE_PATCH_VERSION)
#else
# define NODE_VERSION_STRING NODE_STRINGIFY(NODE_MAJOR_VERSION) "." \
NODE_STRINGIFY(NODE_MINOR_VERSION) "." \
NODE_STRINGIFY(NODE_PATCH_VERSION) "-pre"
#endif
#define NODE_VERSION "v" NODE_VERSION_STRING
#define NODE_VERSION_AT_LEAST(major, minor, patch) \
(( (major) < NODE_MAJOR_VERSION) \
|| ((major) == NODE_MAJOR_VERSION && (minor) < NODE_MINOR_VERSION) \
|| ((major) == NODE_MAJOR_VERSION && (minor) == NODE_MINOR_VERSION && (patch) <= NODE_PATCH_VERSION))
#endif /* NODE_VERSION_H */
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_V8_DEBUG_H_
#define V8_V8_DEBUG_H_
#include "v8.h"
#ifdef _WIN32
typedef int int32_t;
typedef unsigned int uint32_t;
typedef unsigned short uint16_t; // NOLINT
typedef long long int64_t; // NOLINT
// Setup for Windows DLL export/import. See v8.h in this directory for
// information on how to build/use V8 as a DLL.
#if defined(BUILDING_V8_SHARED) && defined(USING_V8_SHARED)
#error both BUILDING_V8_SHARED and USING_V8_SHARED are set - please check the\
build configuration to ensure that at most one of these is set
#endif
#ifdef BUILDING_V8_SHARED
#define EXPORT __declspec(dllexport)
#elif USING_V8_SHARED
#define EXPORT __declspec(dllimport)
#else
#define EXPORT
#endif
#else // _WIN32
// Setup for Linux shared library export. See v8.h in this directory for
// information on how to build/use V8 as shared library.
#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(V8_SHARED)
#define EXPORT __attribute__ ((visibility("default")))
#else // defined(__GNUC__) && (__GNUC__ >= 4)
#define EXPORT
#endif // defined(__GNUC__) && (__GNUC__ >= 4)
#endif // _WIN32
/**
* Debugger support for the V8 JavaScript engine.
*/
namespace v8 {
// Debug events which can occur in the V8 JavaScript engine.
enum DebugEvent {
Break = 1,
Exception = 2,
NewFunction = 3,
BeforeCompile = 4,
AfterCompile = 5,
ScriptCollected = 6,
BreakForCommand = 7
};
class EXPORT Debug {
public:
/**
* A client object passed to the v8 debugger whose ownership will be taken by
* it. v8 is always responsible for deleting the object.
*/
class ClientData {
public:
virtual ~ClientData() {}
};
/**
* A message object passed to the debug message handler.
*/
class Message {
public:
/**
* Check type of message.
*/
virtual bool IsEvent() const = 0;
virtual bool IsResponse() const = 0;
virtual DebugEvent GetEvent() const = 0;
/**
* Indicate whether this is a response to a continue command which will
* start the VM running after this is processed.
*/
virtual bool WillStartRunning() const = 0;
/**
* Access to execution state and event data. Don't store these cross
* callbacks as their content becomes invalid. These objects are from the
* debugger event that started the debug message loop.
*/
virtual Handle<Object> GetExecutionState() const = 0;
virtual Handle<Object> GetEventData() const = 0;
/**
* Get the debugger protocol JSON.
*/
virtual Handle<String> GetJSON() const = 0;
/**
* Get the context active when the debug event happened. Note this is not
* the current active context as the JavaScript part of the debugger is
* running in it's own context which is entered at this point.
*/
virtual Handle<Context> GetEventContext() const = 0;
/**
* Client data passed with the corresponding request if any. This is the
* client_data data value passed into Debug::SendCommand along with the
* request that led to the message or NULL if the message is an event. The
* debugger takes ownership of the data and will delete it even if there is
* no message handler.
*/
virtual ClientData* GetClientData() const = 0;
virtual ~Message() {}
};
/**
* An event details object passed to the debug event listener.
*/
class EventDetails {
public:
/**
* Event type.
*/
virtual DebugEvent GetEvent() const = 0;
/**
* Access to execution state and event data of the debug event. Don't store
* these cross callbacks as their content becomes invalid.
*/
virtual Handle<Object> GetExecutionState() const = 0;
virtual Handle<Object> GetEventData() const = 0;
/**
* Get the context active when the debug event happened. Note this is not
* the current active context as the JavaScript part of the debugger is
* running in it's own context which is entered at this point.
*/
virtual Handle<Context> GetEventContext() const = 0;
/**
* Client data passed with the corresponding callbak whet it was registered.
*/
virtual Handle<Value> GetCallbackData() const = 0;
/**
* Client data passed to DebugBreakForCommand function. The
* debugger takes ownership of the data and will delete it even if
* there is no message handler.
*/
virtual ClientData* GetClientData() const = 0;
virtual ~EventDetails() {}
};
/**
* Debug event callback function.
*
* \param event the type of the debug event that triggered the callback
* (enum DebugEvent)
* \param exec_state execution state (JavaScript object)
* \param event_data event specific data (JavaScript object)
* \param data value passed by the user to SetDebugEventListener
*/
typedef void (*EventCallback)(DebugEvent event,
Handle<Object> exec_state,
Handle<Object> event_data,
Handle<Value> data);
/**
* Debug event callback function.
*
* \param event_details object providing information about the debug event
*
* A EventCallback2 does not take possession of the event data,
* and must not rely on the data persisting after the handler returns.
*/
typedef void (*EventCallback2)(const EventDetails& event_details);
/**
* Debug message callback function.
*
* \param message the debug message handler message object
* \param length length of the message
* \param client_data the data value passed when registering the message handler
* A MessageHandler does not take possession of the message string,
* and must not rely on the data persisting after the handler returns.
*
* This message handler is deprecated. Use MessageHandler2 instead.
*/
typedef void (*MessageHandler)(const uint16_t* message, int length,
ClientData* client_data);
/**
* Debug message callback function.
*
* \param message the debug message handler message object
* A MessageHandler does not take possession of the message data,
* and must not rely on the data persisting after the handler returns.
*/
typedef void (*MessageHandler2)(const Message& message);
/**
* Debug host dispatch callback function.
*/
typedef void (*HostDispatchHandler)();
/**
* Callback function for the host to ensure debug messages are processed.
*/
typedef void (*DebugMessageDispatchHandler)();
// Set a C debug event listener.
static bool SetDebugEventListener(EventCallback that,
Handle<Value> data = Handle<Value>());
static bool SetDebugEventListener2(EventCallback2 that,
Handle<Value> data = Handle<Value>());
// Set a JavaScript debug event listener.
static bool SetDebugEventListener(v8::Handle<v8::Object> that,
Handle<Value> data = Handle<Value>());
// Schedule a debugger break to happen when JavaScript code is run.
static void DebugBreak();
// Remove scheduled debugger break if it has not happened yet.
static void CancelDebugBreak();
// Break execution of JavaScript (this method can be invoked from a
// non-VM thread) for further client command execution on a VM
// thread. Client data is then passed in EventDetails to
// EventCallback at the moment when the VM actually stops.
static void DebugBreakForCommand(ClientData* data = NULL);
// Message based interface. The message protocol is JSON. NOTE the message
// handler thread is not supported any more parameter must be false.
static void SetMessageHandler(MessageHandler handler,
bool message_handler_thread = false);
static void SetMessageHandler2(MessageHandler2 handler);
static void SendCommand(const uint16_t* command, int length,
ClientData* client_data = NULL);
// Dispatch interface.
static void SetHostDispatchHandler(HostDispatchHandler handler,
int period = 100);
/**
* Register a callback function to be called when a debug message has been
* received and is ready to be processed. For the debug messages to be
* processed V8 needs to be entered, and in certain embedding scenarios this
* callback can be used to make sure V8 is entered for the debug message to
* be processed. Note that debug messages will only be processed if there is
* a V8 break. This can happen automatically by using the option
* --debugger-auto-break.
* \param provide_locker requires that V8 acquires v8::Locker for you before
* calling handler
*/
static void SetDebugMessageDispatchHandler(
DebugMessageDispatchHandler handler, bool provide_locker = false);
/**
* Run a JavaScript function in the debugger.
* \param fun the function to call
* \param data passed as second argument to the function
* With this call the debugger is entered and the function specified is called
* with the execution state as the first argument. This makes it possible to
* get access to information otherwise not available during normal JavaScript
* execution e.g. details on stack frames. Receiver of the function call will
* be the debugger context global object, however this is a subject to change.
* The following example show a JavaScript function which when passed to
* v8::Debug::Call will return the current line of JavaScript execution.
*
* \code
* function frame_source_line(exec_state) {
* return exec_state.frame(0).sourceLine();
* }
* \endcode
*/
static Local<Value> Call(v8::Handle<v8::Function> fun,
Handle<Value> data = Handle<Value>());
/**
* Returns a mirror object for the given object.
*/
static Local<Value> GetMirror(v8::Handle<v8::Value> obj);
/**
* Enable the V8 builtin debug agent. The debugger agent will listen on the
* supplied TCP/IP port for remote debugger connection.
* \param name the name of the embedding application
* \param port the TCP/IP port to listen on
* \param wait_for_connection whether V8 should pause on a first statement
* allowing remote debugger to connect before anything interesting happened
*/
static bool EnableAgent(const char* name, int port,
bool wait_for_connection = false);
/**
* Makes V8 process all pending debug messages.
*
* From V8 point of view all debug messages come asynchronously (e.g. from
* remote debugger) but they all must be handled synchronously: V8 cannot
* do 2 things at one time so normal script execution must be interrupted
* for a while.
*
* Generally when message arrives V8 may be in one of 3 states:
* 1. V8 is running script; V8 will automatically interrupt and process all
* pending messages (however auto_break flag should be enabled);
* 2. V8 is suspended on debug breakpoint; in this state V8 is dedicated
* to reading and processing debug messages;
* 3. V8 is not running at all or has called some long-working C++ function;
* by default it means that processing of all debug message will be deferred
* until V8 gets control again; however, embedding application may improve
* this by manually calling this method.
*
* It makes sense to call this method whenever a new debug message arrived and
* V8 is not already running. Method v8::Debug::SetDebugMessageDispatchHandler
* should help with the former condition.
*
* Technically this method in many senses is equivalent to executing empty
* script:
* 1. It does nothing except for processing all pending debug messages.
* 2. It should be invoked with the same precautions and from the same context
* as V8 script would be invoked from, because:
* a. with "evaluate" command it can do whatever normal script can do,
* including all native calls;
* b. no other thread should call V8 while this method is running
* (v8::Locker may be used here).
*
* "Evaluate" debug command behavior currently is not specified in scope
* of this method.
*/
static void ProcessDebugMessages();
/**
* Debugger is running in it's own context which is entered while debugger
* messages are being dispatched. This is an explicit getter for this
* debugger context. Note that the content of the debugger context is subject
* to change.
*/
static Local<Context> GetDebugContext();
};
} // namespace v8
#undef EXPORT
#endif // V8_V8_DEBUG_H_
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef PREPARSER_H
#define PREPARSER_H
#include "v8stdint.h"
#ifdef _WIN32
// Setup for Windows DLL export/import. When building the V8 DLL the
// BUILDING_V8_SHARED needs to be defined. When building a program which uses
// the V8 DLL USING_V8_SHARED needs to be defined. When either building the V8
// static library or building a program which uses the V8 static library neither
// BUILDING_V8_SHARED nor USING_V8_SHARED should be defined.
#if defined(BUILDING_V8_SHARED) && defined(USING_V8_SHARED)
#error both BUILDING_V8_SHARED and USING_V8_SHARED are set - please check the\
build configuration to ensure that at most one of these is set
#endif
#ifdef BUILDING_V8_SHARED
#define V8EXPORT __declspec(dllexport)
#elif USING_V8_SHARED
#define V8EXPORT __declspec(dllimport)
#else
#define V8EXPORT
#endif // BUILDING_V8_SHARED
#else // _WIN32
// Setup for Linux shared library export. There is no need to distinguish
// between building or using the V8 shared library, but we should not
// export symbols when we are building a static library.
#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(V8_SHARED)
#define V8EXPORT __attribute__ ((visibility("default")))
#else // defined(__GNUC__) && (__GNUC__ >= 4)
#define V8EXPORT
#endif // defined(__GNUC__) && (__GNUC__ >= 4)
#endif // _WIN32
namespace v8 {
class PreParserData {
public:
PreParserData(size_t size, const uint8_t* data)
: data_(data), size_(size) { }
// Create a PreParserData value where stack_overflow reports true.
static PreParserData StackOverflow() { return PreParserData(NULL, 0); }
// Whether the pre-parser stopped due to a stack overflow.
// If this is the case, size() and data() should not be used.
bool stack_overflow() { return size_ == 0u; }
// The size of the data in bytes.
size_t size() const { return size_; }
// Pointer to the data.
const uint8_t* data() const { return data_; }
private:
const uint8_t* const data_;
const size_t size_;
};
// Interface for a stream of Unicode characters.
class UnicodeInputStream {
public:
virtual ~UnicodeInputStream();
// Returns the next Unicode code-point in the input, or a negative value when
// there is no more input in the stream.
virtual int32_t Next() = 0;
};
// Preparse a JavaScript program. The source code is provided as a
// UnicodeInputStream. The max_stack_size limits the amount of stack
// space that the preparser is allowed to use. If the preparser uses
// more stack space than the limit provided, the result's stack_overflow()
// method will return true. Otherwise the result contains preparser
// data that can be used by the V8 parser to speed up parsing.
PreParserData V8EXPORT Preparse(UnicodeInputStream* input,
size_t max_stack_size);
} // namespace v8.
#endif // PREPARSER_H
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_V8_PROFILER_H_
#define V8_V8_PROFILER_H_
#include "v8.h"
#ifdef _WIN32
// Setup for Windows DLL export/import. See v8.h in this directory for
// information on how to build/use V8 as a DLL.
#if defined(BUILDING_V8_SHARED) && defined(USING_V8_SHARED)
#error both BUILDING_V8_SHARED and USING_V8_SHARED are set - please check the\
build configuration to ensure that at most one of these is set
#endif
#ifdef BUILDING_V8_SHARED
#define V8EXPORT __declspec(dllexport)
#elif USING_V8_SHARED
#define V8EXPORT __declspec(dllimport)
#else
#define V8EXPORT
#endif
#else // _WIN32
// Setup for Linux shared library export. See v8.h in this directory for
// information on how to build/use V8 as shared library.
#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(V8_SHARED)
#define V8EXPORT __attribute__ ((visibility("default")))
#else // defined(__GNUC__) && (__GNUC__ >= 4)
#define V8EXPORT
#endif // defined(__GNUC__) && (__GNUC__ >= 4)
#endif // _WIN32
/**
* Profiler support for the V8 JavaScript engine.
*/
namespace v8 {
/**
* CpuProfileNode represents a node in a call graph.
*/
class V8EXPORT CpuProfileNode {
public:
/** Returns function name (empty string for anonymous functions.) */
Handle<String> GetFunctionName() const;
/** Returns resource name for script from where the function originates. */
Handle<String> GetScriptResourceName() const;
/**
* Returns the number, 1-based, of the line where the function originates.
* kNoLineNumberInfo if no line number information is available.
*/
int GetLineNumber() const;
/**
* Returns total (self + children) execution time of the function,
* in milliseconds, estimated by samples count.
*/
double GetTotalTime() const;
/**
* Returns self execution time of the function, in milliseconds,
* estimated by samples count.
*/
double GetSelfTime() const;
/** Returns the count of samples where function exists. */
double GetTotalSamplesCount() const;
/** Returns the count of samples where function was currently executing. */
double GetSelfSamplesCount() const;
/** Returns function entry UID. */
unsigned GetCallUid() const;
/** Returns child nodes count of the node. */
int GetChildrenCount() const;
/** Retrieves a child node by index. */
const CpuProfileNode* GetChild(int index) const;
static const int kNoLineNumberInfo = Message::kNoLineNumberInfo;
};
/**
* CpuProfile contains a CPU profile in a form of two call trees:
* - top-down (from main() down to functions that do all the work);
* - bottom-up call graph (in backward direction).
*/
class V8EXPORT CpuProfile {
public:
/** Returns CPU profile UID (assigned by the profiler.) */
unsigned GetUid() const;
/** Returns CPU profile title. */
Handle<String> GetTitle() const;
/** Returns the root node of the bottom up call tree. */
const CpuProfileNode* GetBottomUpRoot() const;
/** Returns the root node of the top down call tree. */
const CpuProfileNode* GetTopDownRoot() const;
};
/**
* Interface for controlling CPU profiling.
*/
class V8EXPORT CpuProfiler {
public:
/**
* A note on security tokens usage. As scripts from different
* origins can run inside a single V8 instance, it is possible to
* have functions from different security contexts intermixed in a
* single CPU profile. To avoid exposing function names belonging to
* other contexts, filtering by security token is performed while
* obtaining profiling results.
*/
/**
* Returns the number of profiles collected (doesn't include
* profiles that are being collected at the moment of call.)
*/
static int GetProfilesCount();
/** Returns a profile by index. */
static const CpuProfile* GetProfile(
int index,
Handle<Value> security_token = Handle<Value>());
/** Returns a profile by uid. */
static const CpuProfile* FindProfile(
unsigned uid,
Handle<Value> security_token = Handle<Value>());
/**
* Starts collecting CPU profile. Title may be an empty string. It
* is allowed to have several profiles being collected at
* once. Attempts to start collecting several profiles with the same
* title are silently ignored. While collecting a profile, functions
* from all security contexts are included in it. The token-based
* filtering is only performed when querying for a profile.
*/
static void StartProfiling(Handle<String> title);
/**
* Stops collecting CPU profile with a given title and returns it.
* If the title given is empty, finishes the last profile started.
*/
static const CpuProfile* StopProfiling(
Handle<String> title,
Handle<Value> security_token = Handle<Value>());
};
class HeapGraphNode;
/**
* HeapSnapshotEdge represents a directed connection between heap
* graph nodes: from retaners to retained nodes.
*/
class V8EXPORT HeapGraphEdge {
public:
enum Type {
kContextVariable = 0, // A variable from a function context.
kElement = 1, // An element of an array.
kProperty = 2, // A named object property.
kInternal = 3, // A link that can't be accessed from JS,
// thus, its name isn't a real property name
// (e.g. parts of a ConsString).
kHidden = 4, // A link that is needed for proper sizes
// calculation, but may be hidden from user.
kShortcut = 5 // A link that must not be followed during
// sizes calculation.
};
/** Returns edge type (see HeapGraphEdge::Type). */
Type GetType() const;
/**
* Returns edge name. This can be a variable name, an element index, or
* a property name.
*/
Handle<Value> GetName() const;
/** Returns origin node. */
const HeapGraphNode* GetFromNode() const;
/** Returns destination node. */
const HeapGraphNode* GetToNode() const;
};
class V8EXPORT HeapGraphPath {
public:
/** Returns the number of edges in the path. */
int GetEdgesCount() const;
/** Returns an edge from the path. */
const HeapGraphEdge* GetEdge(int index) const;
/** Returns origin node. */
const HeapGraphNode* GetFromNode() const;
/** Returns destination node. */
const HeapGraphNode* GetToNode() const;
};
/**
* HeapGraphNode represents a node in a heap graph.
*/
class V8EXPORT HeapGraphNode {
public:
enum Type {
kHidden = 0, // Hidden node, may be filtered when shown to user.
kArray = 1, // An array of elements.
kString = 2, // A string.
kObject = 3, // A JS object (except for arrays and strings).
kCode = 4, // Compiled code.
kClosure = 5, // Function closure.
kRegExp = 6, // RegExp.
kHeapNumber = 7 // Number stored in the heap.
};
/** Returns node type (see HeapGraphNode::Type). */
Type GetType() const;
/**
* Returns node name. Depending on node's type this can be the name
* of the constructor (for objects), the name of the function (for
* closures), string value, or an empty string (for compiled code).
*/
Handle<String> GetName() const;
/**
* Returns node id. For the same heap object, the id remains the same
* across all snapshots. Not applicable to aggregated heap snapshots
* as they only contain aggregated instances.
*/
uint64_t GetId() const;
/**
* Returns the number of instances. Only applicable to aggregated
* heap snapshots.
*/
int GetInstancesCount() const;
/** Returns node's own size, in bytes. */
int GetSelfSize() const;
/**
* Returns node's retained size, in bytes. That is, self + sizes of
* the objects that are reachable only from this object. In other
* words, the size of memory that will be reclaimed having this node
* collected.
*
* Exact retained size calculation has O(N) (number of nodes)
* computational complexity, while approximate has O(1). It is
* assumed that initially heap profiling tools provide approximate
* sizes for all nodes, and then exact sizes are calculated for the
* most 'interesting' nodes.
*/
int GetRetainedSize(bool exact) const;
/** Returns child nodes count of the node. */
int GetChildrenCount() const;
/** Retrieves a child by index. */
const HeapGraphEdge* GetChild(int index) const;
/** Returns retainer nodes count of the node. */
int GetRetainersCount() const;
/** Returns a retainer by index. */
const HeapGraphEdge* GetRetainer(int index) const;
/** Returns the number of simple retaining paths from the root to the node. */
int GetRetainingPathsCount() const;
/** Returns a retaining path by index. */
const HeapGraphPath* GetRetainingPath(int index) const;
/**
* Returns a dominator node. This is the node that participates in every
* path from the snapshot root to the current node.
*/
const HeapGraphNode* GetDominatorNode() const;
};
class V8EXPORT HeapSnapshotsDiff {
public:
/** Returns the root node for added nodes. */
const HeapGraphNode* GetAdditionsRoot() const;
/** Returns the root node for deleted nodes. */
const HeapGraphNode* GetDeletionsRoot() const;
};
/**
* HeapSnapshots record the state of the JS heap at some moment.
*/
class V8EXPORT HeapSnapshot {
public:
enum Type {
kFull = 0, // Heap snapshot with all instances and references.
kAggregated = 1 // Snapshot doesn't contain individual heap entries,
// instead they are grouped by constructor name.
};
enum SerializationFormat {
kJSON = 0 // See format description near 'Serialize' method.
};
/** Returns heap snapshot type. */
Type GetType() const;
/** Returns heap snapshot UID (assigned by the profiler.) */
unsigned GetUid() const;
/** Returns heap snapshot title. */
Handle<String> GetTitle() const;
/** Returns the root node of the heap graph. */
const HeapGraphNode* GetRoot() const;
/** Returns a node by its id. */
const HeapGraphNode* GetNodeById(uint64_t id) const;
/**
* Returns a diff between this snapshot and another one. Only snapshots
* of the same type can be compared.
*/
const HeapSnapshotsDiff* CompareWith(const HeapSnapshot* snapshot) const;
/**
* Prepare a serialized representation of the snapshot. The result
* is written into the stream provided in chunks of specified size.
* The total length of the serialized snapshot is unknown in
* advance, it is can be roughly equal to JS heap size (that means,
* it can be really big - tens of megabytes).
*
* For the JSON format, heap contents are represented as an object
* with the following structure:
*
* {
* snapshot: {title: "...", uid: nnn},
* nodes: [
* meta-info (JSON string),
* nodes themselves
* ],
* strings: [strings]
* }
*
* Outgoing node links are stored after each node. Nodes reference strings
* and other nodes by their indexes in corresponding arrays.
*/
void Serialize(OutputStream* stream, SerializationFormat format) const;
};
/**
* Interface for controlling heap profiling.
*/
class V8EXPORT HeapProfiler {
public:
/** Returns the number of snapshots taken. */
static int GetSnapshotsCount();
/** Returns a snapshot by index. */
static const HeapSnapshot* GetSnapshot(int index);
/** Returns a profile by uid. */
static const HeapSnapshot* FindSnapshot(unsigned uid);
/**
* Takes a heap snapshot and returns it. Title may be an empty string.
* See HeapSnapshot::Type for types description.
*/
static const HeapSnapshot* TakeSnapshot(
Handle<String> title,
HeapSnapshot::Type type = HeapSnapshot::kFull,
ActivityControl* control = NULL);
};
} // namespace v8
#undef V8EXPORT
#endif // V8_V8_PROFILER_H_
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_V8_TEST_H_
#define V8_V8_TEST_H_
#include "v8.h"
#ifdef _WIN32
// Setup for Windows DLL export/import. See v8.h in this directory for
// information on how to build/use V8 as a DLL.
#if defined(BUILDING_V8_SHARED) && defined(USING_V8_SHARED)
#error both BUILDING_V8_SHARED and USING_V8_SHARED are set - please check the\
build configuration to ensure that at most one of these is set
#endif
#ifdef BUILDING_V8_SHARED
#define V8EXPORT __declspec(dllexport)
#elif USING_V8_SHARED
#define V8EXPORT __declspec(dllimport)
#else
#define V8EXPORT
#endif
#else // _WIN32
// Setup for Linux shared library export. See v8.h in this directory for
// information on how to build/use V8 as shared library.
#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(V8_SHARED)
#define V8EXPORT __attribute__ ((visibility("default")))
#else // defined(__GNUC__) && (__GNUC__ >= 4)
#define V8EXPORT
#endif // defined(__GNUC__) && (__GNUC__ >= 4)
#endif // _WIN32
/**
* Testing support for the V8 JavaScript engine.
*/
namespace v8 {
class V8EXPORT Testing {
public:
enum StressType {
kStressTypeOpt,
kStressTypeDeopt
};
/**
* Set the type of stressing to do. The default if not set is kStressTypeOpt.
*/
static void SetStressRunType(StressType type);
/**
* Get the number of runs of a given test that is required to get the full
* stress coverage.
*/
static int GetStressRuns();
/**
* Indicate the number of the run which is about to start. The value of run
* should be between 0 and one less than the result from GetStressRuns()
*/
static void PrepareStressRun(int run);
};
} // namespace v8
#undef V8EXPORT
#endif // V8_V8_TEST_H_
This source diff could not be displayed because it is too large. You can view the blob instead.
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Load definitions of standard types.
#ifndef V8STDINT_H_
#define V8STDINT_H_
#include <stdio.h>
#if defined(_WIN32) && !defined(__MINGW32__)
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef short int16_t; // NOLINT
typedef unsigned short uint16_t; // NOLINT
typedef int int32_t;
typedef unsigned int uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
// intptr_t and friends are defined in crtdefs.h through stdio.h.
#else
#include <stdint.h>
#endif
#endif // V8STDINT_H_
#!/usr/bin/env python
import os, sys
join = os.path.join
wafdir = os.path.dirname(__file__)
w = join(wafdir, 'wafadmin')
t = join(w, 'Tools')
sys.path = [w, t] + sys.path
import Scripting
VERSION="1.5.16"
Scripting.prepare(t, os.getcwd(), VERSION, wafdir)
sys.exit(0)
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2005 (ita)
"""
Dependency tree holder
The class Build holds all the info related to a build:
* file system representation (tree of Node instances)
* various cached objects (task signatures, file scan results, ..)
There is only one Build object at a time (bld singleton)
"""
import os, sys, errno, re, glob, gc, datetime, shutil
try: import cPickle
except: import pickle as cPickle
import Runner, TaskGen, Node, Scripting, Utils, Environment, Task, Logs, Options
from Logs import debug, error, info
from Constants import *
SAVED_ATTRS = 'root srcnode bldnode node_sigs node_deps raw_deps task_sigs id_nodes'.split()
"Build class members to save"
bld = None
"singleton - safe to use when Waf is not used as a library"
class BuildError(Utils.WafError):
def __init__(self, b=None, t=[]):
self.bld = b
self.tasks = t
self.ret = 1
Utils.WafError.__init__(self, self.format_error())
def format_error(self):
lst = ['Build failed:']
for tsk in self.tasks:
txt = tsk.format_error()
if txt: lst.append(txt)
sep = ' '
if len(lst) > 2:
sep = '\n'
return sep.join(lst)
def group_method(fun):
"""
sets a build context method to execute after the current group has finished executing
this is useful for installing build files:
* calling install_files/install_as will fail if called too early
* people do not want to define install method in their task classes
TODO: try it
"""
def f(*k, **kw):
if not k[0].is_install:
return False
postpone = True
if 'postpone' in kw:
postpone = kw['postpone']
del kw['postpone']
# TODO waf 1.6 in theory there should be no reference to the TaskManager internals here
if postpone:
m = k[0].task_manager
if not m.groups: m.add_group()
m.groups[m.current_group].post_funs.append((fun, k, kw))
if not 'cwd' in kw:
kw['cwd'] = k[0].path
else:
fun(*k, **kw)
return f
class BuildContext(Utils.Context):
"holds the dependency tree"
def __init__(self):
# not a singleton, but provided for compatibility
global bld
bld = self
self.task_manager = Task.TaskManager()
# instead of hashing the nodes, we assign them a unique id when they are created
self.id_nodes = 0
self.idx = {}
# map names to environments, the 'default' must be defined
self.all_envs = {}
# ======================================= #
# code for reading the scripts
# project build directory - do not reset() from load_dirs()
self.bdir = ''
# the current directory from which the code is run
# the folder changes everytime a wscript is read
self.path = None
# Manual dependencies.
self.deps_man = Utils.DefaultDict(list)
# ======================================= #
# cache variables
# local cache for absolute paths - cache_node_abspath[variant][node]
self.cache_node_abspath = {}
# list of folders that are already scanned
# so that we do not need to stat them one more time
self.cache_scanned_folders = {}
# list of targets to uninstall for removing the empty folders after uninstalling
self.uninstall = []
# ======================================= #
# tasks and objects
# build dir variants (release, debug, ..)
for v in 'cache_node_abspath task_sigs node_deps raw_deps node_sigs'.split():
var = {}
setattr(self, v, var)
self.cache_dir_contents = {}
self.all_task_gen = []
self.task_gen_cache_names = {}
self.cache_sig_vars = {}
self.log = None
self.root = None
self.srcnode = None
self.bldnode = None
# bind the build context to the nodes in use
# this means better encapsulation and no build context singleton
class node_class(Node.Node):
pass
self.node_class = node_class
self.node_class.__module__ = "Node"
self.node_class.__name__ = "Nodu"
self.node_class.bld = self
self.is_install = None
def __copy__(self):
"nodes are not supposed to be copied"
raise Utils.WafError('build contexts are not supposed to be cloned')
def load(self):
"load the cache from the disk"
try:
env = Environment.Environment(os.path.join(self.cachedir, 'build.config.py'))
except (IOError, OSError):
pass
else:
if env['version'] < HEXVERSION:
raise Utils.WafError('Version mismatch! reconfigure the project')
for t in env['tools']:
self.setup(**t)
try:
gc.disable()
f = data = None
Node.Nodu = self.node_class
try:
f = open(os.path.join(self.bdir, DBFILE), 'rb')
except (IOError, EOFError):
# handle missing file/empty file
pass
try:
if f: data = cPickle.load(f)
except AttributeError:
# handle file of an old Waf version
# that has an attribute which no longer exist
# (e.g. AttributeError: 'module' object has no attribute 'BuildDTO')
if Logs.verbose > 1: raise
if data:
for x in SAVED_ATTRS: setattr(self, x, data[x])
else:
debug('build: Build cache loading failed')
finally:
if f: f.close()
gc.enable()
def save(self):
"store the cache on disk, see self.load"
gc.disable()
self.root.__class__.bld = None
# some people are very nervous with ctrl+c so we have to make a temporary file
Node.Nodu = self.node_class
db = os.path.join(self.bdir, DBFILE)
file = open(db + '.tmp', 'wb')
data = {}
for x in SAVED_ATTRS: data[x] = getattr(self, x)
cPickle.dump(data, file, -1)
file.close()
# do not use shutil.move
try: os.unlink(db)
except OSError: pass
os.rename(db + '.tmp', db)
self.root.__class__.bld = self
gc.enable()
# ======================================= #
def clean(self):
debug('build: clean called')
# does not clean files created during the configuration
precious = set([])
for env in self.all_envs.values():
for x in env[CFG_FILES]:
node = self.srcnode.find_resource(x)
if node:
precious.add(node.id)
def clean_rec(node):
for x in list(node.childs.keys()):
nd = node.childs[x]
tp = nd.id & 3
if tp == Node.DIR:
clean_rec(nd)
elif tp == Node.BUILD:
if nd.id in precious: continue
for env in self.all_envs.values():
try: os.remove(nd.abspath(env))
except OSError: pass
node.childs.__delitem__(x)
clean_rec(self.srcnode)
for v in 'node_sigs node_deps task_sigs raw_deps cache_node_abspath'.split():
setattr(self, v, {})
def compile(self):
"""The cache file is not written if nothing was build at all (build is up to date)"""
debug('build: compile called')
"""
import cProfile, pstats
cProfile.run("import Build\nBuild.bld.flush()", 'profi.txt')
p = pstats.Stats('profi.txt')
p.sort_stats('cumulative').print_stats(80)
"""
self.flush()
#"""
self.generator = Runner.Parallel(self, Options.options.jobs)
def dw(on=True):
if Options.options.progress_bar:
if on: sys.stderr.write(Logs.colors.cursor_on)
else: sys.stderr.write(Logs.colors.cursor_off)
debug('build: executor starting')
back = os.getcwd()
os.chdir(self.bldnode.abspath())
try:
try:
dw(on=False)
self.generator.start()
except KeyboardInterrupt:
dw()
if Runner.TaskConsumer.consumers:
self.save()
raise
except Exception:
dw()
# do not store anything, for something bad happened
raise
else:
dw()
if Runner.TaskConsumer.consumers:
self.save()
if self.generator.error:
raise BuildError(self, self.task_manager.tasks_done)
finally:
os.chdir(back)
def install(self):
"this function is called for both install and uninstall"
debug('build: install called')
self.flush()
# remove empty folders after uninstalling
if self.is_install < 0:
lst = []
for x in self.uninstall:
dir = os.path.dirname(x)
if not dir in lst: lst.append(dir)
lst.sort()
lst.reverse()
nlst = []
for y in lst:
x = y
while len(x) > 4:
if not x in nlst: nlst.append(x)
x = os.path.dirname(x)
nlst.sort()
nlst.reverse()
for x in nlst:
try: os.rmdir(x)
except OSError: pass
def new_task_gen(self, *k, **kw):
if self.task_gen_cache_names:
self.task_gen_cache_names = {}
kw['bld'] = self
if len(k) == 0:
ret = TaskGen.task_gen(*k, **kw)
else:
cls_name = k[0]
try: cls = TaskGen.task_gen.classes[cls_name]
except KeyError: raise Utils.WscriptError('%s is not a valid task generator -> %s' %
(cls_name, [x for x in TaskGen.task_gen.classes]))
ret = cls(*k, **kw)
return ret
def __call__(self, *k, **kw):
if self.task_gen_cache_names:
self.task_gen_cache_names = {}
kw['bld'] = self
return TaskGen.task_gen(*k, **kw)
def load_envs(self):
try:
lst = Utils.listdir(self.cachedir)
except OSError, e:
if e.errno == errno.ENOENT:
raise Utils.WafError('The project was not configured: run "waf configure" first!')
else:
raise
if not lst:
raise Utils.WafError('The cache directory is empty: reconfigure the project')
for file in lst:
if file.endswith(CACHE_SUFFIX):
env = Environment.Environment(os.path.join(self.cachedir, file))
name = file[:-len(CACHE_SUFFIX)]
self.all_envs[name] = env
self.init_variants()
for env in self.all_envs.values():
for f in env[CFG_FILES]:
newnode = self.path.find_or_declare(f)
try:
hash = Utils.h_file(newnode.abspath(env))
except (IOError, AttributeError):
error("cannot find "+f)
hash = SIG_NIL
self.node_sigs[env.variant()][newnode.id] = hash
# TODO: hmmm, these nodes are removed from the tree when calling rescan()
self.bldnode = self.root.find_dir(self.bldnode.abspath())
self.path = self.srcnode = self.root.find_dir(self.srcnode.abspath())
self.cwd = self.bldnode.abspath()
def setup(self, tool, tooldir=None, funs=None):
"setup tools for build process"
if isinstance(tool, list):
for i in tool: self.setup(i, tooldir)
return
if not tooldir: tooldir = Options.tooldir
module = Utils.load_tool(tool, tooldir)
if hasattr(module, "setup"): module.setup(self)
def init_variants(self):
debug('build: init variants')
lstvariants = []
for env in self.all_envs.values():
if not env.variant() in lstvariants:
lstvariants.append(env.variant())
self.lst_variants = lstvariants
debug('build: list of variants is %r', lstvariants)
for name in lstvariants+[0]:
for v in 'node_sigs cache_node_abspath'.split():
var = getattr(self, v)
if not name in var:
var[name] = {}
# ======================================= #
# node and folder handling
# this should be the main entry point
def load_dirs(self, srcdir, blddir, load_cache=1):
"this functions should be the start of everything"
assert(os.path.isabs(srcdir))
assert(os.path.isabs(blddir))
self.cachedir = os.path.join(blddir, CACHE_DIR)
if srcdir == blddir:
raise Utils.WafError("build dir must be different from srcdir: %s <-> %s " % (srcdir, blddir))
self.bdir = blddir
# try to load the cache file, if it does not exist, nothing happens
self.load()
if not self.root:
Node.Nodu = self.node_class
self.root = Node.Nodu('', None, Node.DIR)
if not self.srcnode:
self.srcnode = self.root.ensure_dir_node_from_path(srcdir)
debug('build: srcnode is %s and srcdir %s', self.srcnode.name, srcdir)
self.path = self.srcnode
# create this build dir if necessary
try: os.makedirs(blddir)
except OSError: pass
if not self.bldnode:
self.bldnode = self.root.ensure_dir_node_from_path(blddir)
self.init_variants()
def rescan(self, src_dir_node):
"""
look the contents of a (folder)node and update its list of childs
The intent is to perform the following steps
* remove the nodes for the files that have disappeared
* remove the signatures for the build files that have disappeared
* cache the results of os.listdir
* create the build folder equivalent (mkdir) for each variant
src/bar -> build/default/src/bar, build/release/src/bar
when a folder in the source directory is removed, we do not check recursively
to remove the unused nodes. To do that, call 'waf clean' and build again.
"""
# do not rescan over and over again
# TODO use a single variable in waf 1.6
if self.cache_scanned_folders.get(src_dir_node.id, None): return
self.cache_scanned_folders[src_dir_node.id] = True
# TODO remove in waf 1.6
if hasattr(self, 'repository'): self.repository(src_dir_node)
if not src_dir_node.name and sys.platform == 'win32':
# the root has no name, contains drive letters, and cannot be listed
return
# first, take the case of the source directory
parent_path = src_dir_node.abspath()
try:
lst = set(Utils.listdir(parent_path))
except OSError:
lst = set([])
# TODO move this at the bottom
self.cache_dir_contents[src_dir_node.id] = lst
# hash the existing source files, remove the others
cache = self.node_sigs[0]
for x in src_dir_node.childs.values():
if x.id & 3 != Node.FILE: continue
if x.name in lst:
try:
cache[x.id] = Utils.h_file(x.abspath())
except IOError:
raise Utils.WafError('The file %s is not readable or has become a dir' % x.abspath())
else:
try: del cache[x.id]
except KeyError: pass
del src_dir_node.childs[x.name]
# first obtain the differences between srcnode and src_dir_node
h1 = self.srcnode.height()
h2 = src_dir_node.height()
lst = []
child = src_dir_node
while h2 > h1:
lst.append(child.name)
child = child.parent
h2 -= 1
lst.reverse()
# list the files in the build dirs
try:
for variant in self.lst_variants:
sub_path = os.path.join(self.bldnode.abspath(), variant , *lst)
self.listdir_bld(src_dir_node, sub_path, variant)
except OSError:
# listdir failed, remove the build node signatures for all variants
for node in src_dir_node.childs.values():
if node.id & 3 != Node.BUILD:
continue
for dct in self.node_sigs.values():
if node.id in dct:
dct.__delitem__(node.id)
# the policy is to avoid removing nodes representing directories
src_dir_node.childs.__delitem__(node.name)
for variant in self.lst_variants:
sub_path = os.path.join(self.bldnode.abspath(), variant , *lst)
try:
os.makedirs(sub_path)
except OSError:
pass
# ======================================= #
def listdir_src(self, parent_node):
"""do not use, kept for compatibility"""
pass
def remove_node(self, node):
"""do not use, kept for compatibility"""
pass
def listdir_bld(self, parent_node, path, variant):
"""in this method we do not add timestamps but we remove them
when the files no longer exist (file removed in the build dir)"""
i_existing_nodes = [x for x in parent_node.childs.values() if x.id & 3 == Node.BUILD]
lst = set(Utils.listdir(path))
node_names = set([x.name for x in i_existing_nodes])
remove_names = node_names - lst
# remove the stamps of the build nodes that no longer exist on the filesystem
ids_to_remove = [x.id for x in i_existing_nodes if x.name in remove_names]
cache = self.node_sigs[variant]
for nid in ids_to_remove:
if nid in cache:
cache.__delitem__(nid)
def get_env(self):
return self.env_of_name('default')
def set_env(self, name, val):
self.all_envs[name] = val
env = property(get_env, set_env)
def add_manual_dependency(self, path, value):
if isinstance(path, Node.Node):
node = path
elif os.path.isabs(path):
node = self.root.find_resource(path)
else:
node = self.path.find_resource(path)
self.deps_man[node.id].append(value)
def launch_node(self):
"""return the launch directory as a node"""
# p_ln is kind of private, but public in case if
try:
return self.p_ln
except AttributeError:
self.p_ln = self.root.find_dir(Options.launch_dir)
return self.p_ln
def glob(self, pattern, relative=True):
"files matching the pattern, seen from the current folder"
path = self.path.abspath()
files = [self.root.find_resource(x) for x in glob.glob(path+os.sep+pattern)]
if relative:
files = [x.path_to_parent(self.path) for x in files if x]
else:
files = [x.abspath() for x in files if x]
return files
## the following methods are candidates for the stable apis ##
def add_group(self, *k):
self.task_manager.add_group(*k)
def set_group(self, *k, **kw):
self.task_manager.set_group(*k, **kw)
def hash_env_vars(self, env, vars_lst):
"""hash environment variables
['CXX', ..] -> [env['CXX'], ..] -> md5()"""
# ccroot objects use the same environment for building the .o at once
# the same environment and the same variables are used
idx = str(id(env)) + str(vars_lst)
try: return self.cache_sig_vars[idx]
except KeyError: pass
lst = [str(env[a]) for a in vars_lst]
ret = Utils.h_list(lst)
debug('envhash: %r %r', ret, lst)
# next time
self.cache_sig_vars[idx] = ret
return ret
def name_to_obj(self, name, env):
"""retrieve a task generator from its name or its target name
remember that names must be unique"""
cache = self.task_gen_cache_names
if not cache:
# create the index lazily
for x in self.all_task_gen:
vt = x.env.variant() + '_'
if x.name:
cache[vt + x.name] = x
else:
if isinstance(x.target, str):
target = x.target
else:
target = ' '.join(x.target)
v = vt + target
if not cache.get(v, None):
cache[v] = x
return cache.get(env.variant() + '_' + name, None)
def flush(self, all=1):
"""tell the task generators to create the tasks"""
self.ini = datetime.datetime.now()
# force the initialization of the mapping name->object in flush
# name_to_obj can be used in userland scripts, in that case beware of incomplete mapping
self.task_gen_cache_names = {}
self.name_to_obj('', self.env)
debug('build: delayed operation TaskGen.flush() called')
if Options.options.compile_targets:
debug('task_gen: posting objects listed in compile_targets')
# ensure the target names exist, fail before any post()
target_objects = Utils.DefaultDict(list)
for target_name in Options.options.compile_targets.split(','):
# trim target_name (handle cases when the user added spaces to targets)
target_name = target_name.strip()
for env in self.all_envs.values():
obj = self.name_to_obj(target_name, env)
if obj:
target_objects[target_name].append(obj)
if not target_name in target_objects and all:
raise Utils.WafError("target '%s' does not exist" % target_name)
to_compile = []
for x in target_objects.values():
for y in x:
to_compile.append(id(y))
# tasks must be posted in order of declaration
# we merely apply a filter to discard the ones we are not interested in
for i in xrange(len(self.task_manager.groups)):
g = self.task_manager.groups[i]
self.task_manager.current_group = i
if Logs.verbose:
Logs.debug('group: group %s' % ([x for x in self.task_manager.groups_names if id(self.task_manager.groups_names[x]) == id(g)][0]))
for tg in g.tasks_gen:
if id(tg) in to_compile:
if Logs.verbose:
Logs.debug('group: %s' % tg)
tg.post()
else:
debug('task_gen: posting objects (normal)')
ln = self.launch_node()
# if the build is started from the build directory, do as if it was started from the top-level
# for the pretty-printing (Node.py), the two lines below cannot be moved to Build::launch_node
if ln.is_child_of(self.bldnode) or not ln.is_child_of(self.srcnode):
ln = self.srcnode
# if the project file is located under the source directory, build all targets by default
# else 'waf configure build' does nothing
proj_node = self.root.find_dir(os.path.split(Utils.g_module.root_path)[0])
if proj_node.id != self.srcnode.id:
ln = self.srcnode
for i in xrange(len(self.task_manager.groups)):
g = self.task_manager.groups[i]
self.task_manager.current_group = i
if Logs.verbose:
Logs.debug('group: group %s' % ([x for x in self.task_manager.groups_names if id(self.task_manager.groups_names[x]) == id(g)][0]))
for tg in g.tasks_gen:
if not tg.path.is_child_of(ln):
continue
if Logs.verbose:
Logs.debug('group: %s' % tg)
tg.post()
def env_of_name(self, name):
try:
return self.all_envs[name]
except KeyError:
error('no such environment: '+name)
return None
def progress_line(self, state, total, col1, col2):
n = len(str(total))
Utils.rot_idx += 1
ind = Utils.rot_chr[Utils.rot_idx % 4]
ini = self.ini
pc = (100.*state)/total
eta = Utils.get_elapsed_time(ini)
fs = "[%%%dd/%%%dd][%%s%%2d%%%%%%s][%s][" % (n, n, ind)
left = fs % (state, total, col1, pc, col2)
right = '][%s%s%s]' % (col1, eta, col2)
cols = Utils.get_term_cols() - len(left) - len(right) + 2*len(col1) + 2*len(col2)
if cols < 7: cols = 7
ratio = int((cols*state)/total) - 1
bar = ('='*ratio+'>').ljust(cols)
msg = Utils.indicator % (left, bar, right)
return msg
# do_install is not used anywhere
def do_install(self, src, tgt, chmod=O644):
"""returns true if the file was effectively installed or uninstalled, false otherwise"""
if self.is_install > 0:
if not Options.options.force:
# check if the file is already there to avoid a copy
try:
st1 = os.stat(tgt)
st2 = os.stat(src)
except OSError:
pass
else:
# same size and identical timestamps -> make no copy
if st1.st_mtime >= st2.st_mtime and st1.st_size == st2.st_size:
return False
srclbl = src.replace(self.srcnode.abspath(None)+os.sep, '')
info("* installing %s as %s" % (srclbl, tgt))
# following is for shared libs and stale inodes (-_-)
try: os.remove(tgt)
except OSError: pass
try:
shutil.copy2(src, tgt)
os.chmod(tgt, chmod)
except IOError:
try:
os.stat(src)
except (OSError, IOError):
error('File %r does not exist' % src)
raise Utils.WafError('Could not install the file %r' % tgt)
return True
elif self.is_install < 0:
info("* uninstalling %s" % tgt)
self.uninstall.append(tgt)
try:
os.remove(tgt)
except OSError, e:
if e.errno != errno.ENOENT:
if not getattr(self, 'uninstall_error', None):
self.uninstall_error = True
Logs.warn('build: some files could not be uninstalled (retry with -vv to list them)')
if Logs.verbose > 1:
Logs.warn('could not remove %s (error code %r)' % (e.filename, e.errno))
return True
red = re.compile(r"^([A-Za-z]:)?[/\\\\]*")
def get_install_path(self, path, env=None):
"installation path prefixed by the destdir, the variables like in '${PREFIX}/bin' are substituted"
if not env: env = self.env
destdir = env.get_destdir()
path = path.replace('/', os.sep)
destpath = Utils.subst_vars(path, env)
if destdir:
destpath = os.path.join(destdir, self.red.sub('', destpath))
return destpath
def install_dir(self, path, env=None):
"""
create empty folders for the installation (very rarely used)
"""
if env:
assert isinstance(env, Environment.Environment), "invalid parameter"
else:
env = self.env
if not path:
return []
destpath = self.get_install_path(path, env)
if self.is_install > 0:
info('* creating %s' % destpath)
Utils.check_dir(destpath)
elif self.is_install < 0:
info('* removing %s' % destpath)
self.uninstall.append(destpath + '/xxx') # yes, ugly
def install_files(self, path, files, env=None, chmod=O644, relative_trick=False, cwd=None):
"""To install files only after they have been built, put the calls in a method named
post_build on the top-level wscript
The files must be a list and contain paths as strings or as Nodes
The relative_trick flag can be set to install folders, use bld.path.ant_glob() with it
"""
if env:
assert isinstance(env, Environment.Environment), "invalid parameter"
else:
env = self.env
if not path: return []
if not cwd:
cwd = self.path
if isinstance(files, str) and '*' in files:
gl = cwd.abspath() + os.sep + files
lst = glob.glob(gl)
else:
lst = Utils.to_list(files)
if not getattr(lst, '__iter__', False):
lst = [lst]
destpath = self.get_install_path(path, env)
Utils.check_dir(destpath)
installed_files = []
for filename in lst:
if isinstance(filename, str) and os.path.isabs(filename):
alst = Utils.split_path(filename)
destfile = os.path.join(destpath, alst[-1])
else:
if isinstance(filename, Node.Node):
nd = filename
else:
nd = cwd.find_resource(filename)
if not nd:
raise Utils.WafError("Unable to install the file %r (not found in %s)" % (filename, cwd))
if relative_trick:
destfile = os.path.join(destpath, filename)
Utils.check_dir(os.path.dirname(destfile))
else:
destfile = os.path.join(destpath, nd.name)
filename = nd.abspath(env)
if self.do_install(filename, destfile, chmod):
installed_files.append(destfile)
return installed_files
def install_as(self, path, srcfile, env=None, chmod=O644, cwd=None):
"""
srcfile may be a string or a Node representing the file to install
returns True if the file was effectively installed, False otherwise
"""
if env:
assert isinstance(env, Environment.Environment), "invalid parameter"
else:
env = self.env
if not path:
raise Utils.WafError("where do you want to install %r? (%r?)" % (srcfile, path))
if not cwd:
cwd = self.path
destpath = self.get_install_path(path, env)
dir, name = os.path.split(destpath)
Utils.check_dir(dir)
# the source path
if isinstance(srcfile, Node.Node):
src = srcfile.abspath(env)
else:
src = srcfile
if not os.path.isabs(srcfile):
node = cwd.find_resource(srcfile)
if not node:
raise Utils.WafError("Unable to install the file %r (not found in %s)" % (srcfile, cwd))
src = node.abspath(env)
return self.do_install(src, destpath, chmod)
def symlink_as(self, path, src, env=None, cwd=None):
"""example: bld.symlink_as('${PREFIX}/lib/libfoo.so', 'libfoo.so.1.2.3') """
if sys.platform == 'win32':
# well, this *cannot* work
return
if not path:
raise Utils.WafError("where do you want to install %r? (%r?)" % (src, path))
tgt = self.get_install_path(path, env)
dir, name = os.path.split(tgt)
Utils.check_dir(dir)
if self.is_install > 0:
link = False
if not os.path.islink(tgt):
link = True
elif os.readlink(tgt) != src:
link = True
if link:
try: os.remove(tgt)
except OSError: pass
info('* symlink %s (-> %s)' % (tgt, src))
os.symlink(src, tgt)
return 0
else: # UNINSTALL
try:
info('* removing %s' % (tgt))
os.remove(tgt)
return 0
except OSError:
return 1
def exec_command(self, cmd, **kw):
# 'runner' zone is printed out for waf -v, see wafadmin/Options.py
debug('runner: system command -> %s', cmd)
if self.log:
self.log.write('%s\n' % cmd)
kw['log'] = self.log
try:
if not kw.get('cwd', None):
kw['cwd'] = self.cwd
except AttributeError:
self.cwd = kw['cwd'] = self.bldnode.abspath()
return Utils.exec_command(cmd, **kw)
def printout(self, s):
f = self.log or sys.stderr
f.write(s)
f.flush()
def add_subdirs(self, dirs):
self.recurse(dirs, 'build')
def pre_recurse(self, name_or_mod, path, nexdir):
if not hasattr(self, 'oldpath'):
self.oldpath = []
self.oldpath.append(self.path)
self.path = self.root.find_dir(nexdir)
return {'bld': self, 'ctx': self}
def post_recurse(self, name_or_mod, path, nexdir):
self.path = self.oldpath.pop()
###### user-defined behaviour
def pre_build(self):
if hasattr(self, 'pre_funs'):
for m in self.pre_funs:
m(self)
def post_build(self):
if hasattr(self, 'post_funs'):
for m in self.post_funs:
m(self)
def add_pre_fun(self, meth):
try: self.pre_funs.append(meth)
except AttributeError: self.pre_funs = [meth]
def add_post_fun(self, meth):
try: self.post_funs.append(meth)
except AttributeError: self.post_funs = [meth]
def use_the_magic(self):
Task.algotype = Task.MAXPARALLEL
Task.file_deps = Task.extract_deps
self.magic = True
install_as = group_method(install_as)
install_files = group_method(install_files)
symlink_as = group_method(symlink_as)
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2005-2008 (ita)
"""
Configuration system
A configuration instance is created when "waf configure" is called, it is used to:
* create data dictionaries (Environment instances)
* store the list of modules to import
The old model (copied from Scons) was to store logic (mapping file extensions to functions)
along with the data. In Waf a way was found to separate that logic by adding an indirection
layer (storing the names in the Environment instances)
In the new model, the logic is more object-oriented, and the user scripts provide the
logic. The data files (Environments) must contain configuration data only (flags, ..).
Note: the c/c++ related code is in the module config_c
"""
import os, shlex, sys, time
try: import cPickle
except ImportError: import pickle as cPickle
import Environment, Utils, Options, Logs
from Logs import warn
from Constants import *
try:
from urllib import request
except:
from urllib import urlopen
else:
urlopen = request.urlopen
conf_template = '''# project %(app)s configured on %(now)s by
# waf %(wafver)s (abi %(abi)s, python %(pyver)x on %(systype)s)
# using %(args)s
#
'''
class ConfigurationError(Utils.WscriptError):
pass
autoconfig = False
"reconfigure the project automatically"
def find_file(filename, path_list):
"""find a file in a list of paths
@param filename: name of the file to search for
@param path_list: list of directories to search
@return: the first occurrence filename or '' if filename could not be found
"""
for directory in Utils.to_list(path_list):
if os.path.exists(os.path.join(directory, filename)):
return directory
return ''
def find_program_impl(env, filename, path_list=[], var=None, environ=None):
"""find a program in folders path_lst, and sets env[var]
@param env: environment
@param filename: name of the program to search for
@param path_list: list of directories to search for filename
@param var: environment value to be checked for in env or os.environ
@return: either the value that is referenced with [var] in env or os.environ
or the first occurrence filename or '' if filename could not be found
"""
if not environ:
environ = os.environ
try: path_list = path_list.split()
except AttributeError: pass
if var:
if env[var]: return env[var]
if var in environ: env[var] = environ[var]
if not path_list: path_list = environ.get('PATH', '').split(os.pathsep)
ext = (Options.platform == 'win32') and '.exe,.com,.bat,.cmd' or ''
for y in [filename+x for x in ext.split(',')]:
for directory in path_list:
x = os.path.join(directory, y)
if os.path.isfile(x):
if var: env[var] = x
return x
return ''
class ConfigurationContext(Utils.Context):
tests = {}
error_handlers = []
def __init__(self, env=None, blddir='', srcdir=''):
self.env = None
self.envname = ''
self.environ = dict(os.environ)
self.line_just = 40
self.blddir = blddir
self.srcdir = srcdir
self.all_envs = {}
# curdir: necessary for recursion
self.cwd = self.curdir = os.getcwd()
self.tools = [] # tools loaded in the configuration, and that will be loaded when building
self.setenv(DEFAULT)
self.lastprog = ''
self.hash = 0
self.files = []
self.tool_cache = []
if self.blddir:
self.post_init()
def post_init(self):
self.cachedir = os.path.join(self.blddir, CACHE_DIR)
path = os.path.join(self.blddir, WAF_CONFIG_LOG)
try: os.unlink(path)
except (OSError, IOError): pass
try:
self.log = open(path, 'w')
except (OSError, IOError):
self.fatal('could not open %r for writing' % path)
app = Utils.g_module.APPNAME
if app:
ver = getattr(Utils.g_module, 'VERSION', '')
if ver:
app = "%s (%s)" % (app, ver)
now = time.ctime()
pyver = sys.hexversion
systype = sys.platform
args = " ".join(sys.argv)
wafver = WAFVERSION
abi = ABI
self.log.write(conf_template % vars())
def __del__(self):
"""cleanup function: close config.log"""
# may be ran by the gc, not always after initialization
if hasattr(self, 'log') and self.log:
self.log.close()
def fatal(self, msg):
raise ConfigurationError(msg)
def check_tool(self, input, tooldir=None, funs=None):
"load a waf tool"
tools = Utils.to_list(input)
if tooldir: tooldir = Utils.to_list(tooldir)
for tool in tools:
tool = tool.replace('++', 'xx')
if tool == 'java': tool = 'javaw'
if tool.lower() == 'unittest': tool = 'unittestw'
# avoid loading the same tool more than once with the same functions
# used by composite projects
mag = (tool, id(self.env), funs)
if mag in self.tool_cache:
continue
self.tool_cache.append(mag)
if not tooldir:
# check if the tool exists in the Tools or 3rdparty folders
_Tools = Options.tooldir[0]
_3rdparty = os.sep.join((_Tools, '..', '3rdparty'))
for d in (_Tools, _3rdparty):
lst = os.listdir(d)
if tool + '.py' in lst:
break
else:
# try to download the tool from the repository then
for x in Utils.to_list(Options.remote_repo):
for sub in ['branches/waf-%s/wafadmin/3rdparty' % WAFVERSION, 'trunk/wafadmin/3rdparty']:
url = '/'.join((x, sub, tool + '.py'))
try:
web = urlopen(url)
if web.getcode() != 200:
continue
except Exception, e:
# on python3 urlopen throws an exception
continue
else:
try:
loc = open(_3rdparty + os.sep + tool + '.py', 'wb')
loc.write(web.read())
web.close()
finally:
loc.close()
Logs.warn('downloaded %s from %s' % (tool, url))
else:
break
module = Utils.load_tool(tool, tooldir)
if funs is not None:
self.eval_rules(funs)
else:
func = getattr(module, 'detect', None)
if func:
if type(func) is type(find_file): func(self)
else: self.eval_rules(func)
self.tools.append({'tool':tool, 'tooldir':tooldir, 'funs':funs})
def sub_config(self, k):
"executes the configure function of a wscript module"
self.recurse(k, name='configure')
def pre_recurse(self, name_or_mod, path, nexdir):
return {'conf': self, 'ctx': self}
def post_recurse(self, name_or_mod, path, nexdir):
if not autoconfig:
return
self.hash = hash((self.hash, getattr(name_or_mod, 'waf_hash_val', name_or_mod)))
self.files.append(path)
def store(self, file=''):
"save the config results into the cache file"
if not os.path.isdir(self.cachedir):
os.makedirs(self.cachedir)
if not file:
file = open(os.path.join(self.cachedir, 'build.config.py'), 'w')
file.write('version = 0x%x\n' % HEXVERSION)
file.write('tools = %r\n' % self.tools)
file.close()
if not self.all_envs:
self.fatal('nothing to store in the configuration context!')
for key in self.all_envs:
tmpenv = self.all_envs[key]
tmpenv.store(os.path.join(self.cachedir, key + CACHE_SUFFIX))
def set_env_name(self, name, env):
"add a new environment called name"
self.all_envs[name] = env
return env
def retrieve(self, name, fromenv=None):
"retrieve an environment called name"
try:
env = self.all_envs[name]
except KeyError:
env = Environment.Environment()
env['PREFIX'] = os.path.abspath(os.path.expanduser(Options.options.prefix))
self.all_envs[name] = env
else:
if fromenv: warn("The environment %s may have been configured already" % name)
return env
def setenv(self, name):
"enable the environment called name"
self.env = self.retrieve(name)
self.envname = name
def add_os_flags(self, var, dest=None):
# do not use 'get' to make certain the variable is not defined
try: self.env.append_value(dest or var, Utils.to_list(self.environ[var]))
except KeyError: pass
def check_message_1(self, sr):
self.line_just = max(self.line_just, len(sr))
for x in ('\n', self.line_just * '-', '\n', sr, '\n'):
self.log.write(x)
Utils.pprint('NORMAL', "%s :" % sr.ljust(self.line_just), sep='')
def check_message_2(self, sr, color='GREEN'):
self.log.write(sr)
self.log.write('\n')
Utils.pprint(color, sr)
def check_message(self, th, msg, state, option=''):
sr = 'Checking for %s %s' % (th, msg)
self.check_message_1(sr)
p = self.check_message_2
if state: p('ok ' + str(option))
else: p('not found', 'YELLOW')
# FIXME remove in waf 1.6
# the parameter 'option' is not used (kept for compatibility)
def check_message_custom(self, th, msg, custom, option='', color='PINK'):
sr = 'Checking for %s %s' % (th, msg)
self.check_message_1(sr)
self.check_message_2(custom, color)
def find_program(self, filename, path_list=[], var=None, mandatory=False):
"wrapper that adds a configuration message"
ret = None
if var:
if self.env[var]:
ret = self.env[var]
elif var in os.environ:
ret = os.environ[var]
if not isinstance(filename, list): filename = [filename]
if not ret:
for x in filename:
ret = find_program_impl(self.env, x, path_list, var, environ=self.environ)
if ret: break
self.check_message_1('Checking for program %s' % ' or '.join(filename))
self.log.write(' find program=%r paths=%r var=%r\n -> %r\n' % (filename, path_list, var, ret))
if ret:
Utils.pprint('GREEN', str(ret))
else:
Utils.pprint('YELLOW', 'not found')
if mandatory:
self.fatal('The program %r is required' % filename)
if var:
self.env[var] = ret
return ret
def cmd_to_list(self, cmd):
"commands may be written in pseudo shell like 'ccache g++'"
if isinstance(cmd, str) and cmd.find(' '):
try:
os.stat(cmd)
except OSError:
return shlex.split(cmd)
else:
return [cmd]
return cmd
def __getattr__(self, name):
r = self.__class__.__dict__.get(name, None)
if r: return r
if name and name.startswith('require_'):
for k in ['check_', 'find_']:
n = name.replace('require_', k)
ret = self.__class__.__dict__.get(n, None)
if ret:
def run(*k, **kw):
r = ret(self, *k, **kw)
if not r:
self.fatal('requirement failure')
return r
return run
self.fatal('No such method %r' % name)
def eval_rules(self, rules):
self.rules = Utils.to_list(rules)
for x in self.rules:
f = getattr(self, x)
if not f: self.fatal("No such method '%s'." % x)
try:
f()
except Exception, e:
ret = self.err_handler(x, e)
if ret == BREAK:
break
elif ret == CONTINUE:
continue
else:
self.fatal(e)
def err_handler(self, fun, error):
pass
def conf(f):
"decorator: attach new configuration functions"
setattr(ConfigurationContext, f.__name__, f)
return f
def conftest(f):
"decorator: attach new configuration tests (registered as strings)"
ConfigurationContext.tests[f.__name__] = f
return conf(f)
#!/usr/bin/env python
# encoding: utf-8
# Yinon dot me gmail 2008
"""
these constants are somewhat public, try not to mess them
maintainer: the version number is updated from the top-level wscript file
"""
# do not touch these three lines, they are updated automatically
HEXVERSION=0x105016
WAFVERSION="1.5.16"
WAFREVISION = "7610:7647M"
ABI = 7
# permissions
O644 = 420
O755 = 493
MAXJOBS = 99999999
CACHE_DIR = 'c4che'
CACHE_SUFFIX = '.cache.py'
DBFILE = '.wafpickle-%d' % ABI
WSCRIPT_FILE = 'wscript'
WSCRIPT_BUILD_FILE = 'wscript_build'
WAF_CONFIG_LOG = 'config.log'
WAF_CONFIG_H = 'config.h'
SIG_NIL = 'iluvcuteoverload'
VARIANT = '_VARIANT_'
DEFAULT = 'default'
SRCDIR = 'srcdir'
BLDDIR = 'blddir'
APPNAME = 'APPNAME'
VERSION = 'VERSION'
DEFINES = 'defines'
UNDEFINED = ()
BREAK = "break"
CONTINUE = "continue"
# task scheduler options
JOBCONTROL = "JOBCONTROL"
MAXPARALLEL = "MAXPARALLEL"
NORMAL = "NORMAL"
# task state
NOT_RUN = 0
MISSING = 1
CRASHED = 2
EXCEPTION = 3
SKIPPED = 8
SUCCESS = 9
ASK_LATER = -1
SKIP_ME = -2
RUN_ME = -3
LOG_FORMAT = "%(asctime)s %(c1)s%(zone)s%(c2)s %(message)s"
HOUR_FORMAT = "%H:%M:%S"
TEST_OK = True
CFG_FILES = 'cfg_files'
# positive '->' install
# negative '<-' uninstall
INSTALL = 1337
UNINSTALL = -1337
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2005 (ita)
"""Environment representation
There is one gotcha: getitem returns [] if the contents evals to False
This means env['foo'] = {}; print env['foo'] will print [] not {}
"""
import os, copy, re
import Logs, Options, Utils
from Constants import *
re_imp = re.compile('^(#)*?([^#=]*?)\ =\ (.*?)$', re.M)
class Environment(object):
"""A safe-to-use dictionary, but do not attach functions to it please (break cPickle)
An environment instance can be stored into a file and loaded easily
"""
__slots__ = ("table", "parent")
def __init__(self, filename=None):
self.table = {}
#self.parent = None
if filename:
self.load(filename)
def __contains__(self, key):
if key in self.table: return True
try: return self.parent.__contains__(key)
except AttributeError: return False # parent may not exist
def __str__(self):
keys = set()
cur = self
while cur:
keys.update(cur.table.keys())
cur = getattr(cur, 'parent', None)
keys = list(keys)
keys.sort()
return "\n".join(["%r %r" % (x, self.__getitem__(x)) for x in keys])
def __getitem__(self, key):
try:
while 1:
x = self.table.get(key, None)
if not x is None:
return x
self = self.parent
except AttributeError:
return []
def __setitem__(self, key, value):
self.table[key] = value
def __delitem__(self, key):
del self.table[key]
def pop(self, key, *args):
if len(args):
return self.table.pop(key, *args)
return self.table.pop(key)
def set_variant(self, name):
self.table[VARIANT] = name
def variant(self):
try:
while 1:
x = self.table.get(VARIANT, None)
if not x is None:
return x
self = self.parent
except AttributeError:
return DEFAULT
def copy(self):
# TODO waf 1.6 rename this method derive, #368
newenv = Environment()
newenv.parent = self
return newenv
def detach(self):
"""TODO try it
modifying the original env will not change the copy"""
tbl = self.get_merged_dict()
try:
delattr(self, 'parent')
except AttributeError:
pass
else:
keys = tbl.keys()
for x in keys:
tbl[x] = copy.deepcopy(tbl[x])
self.table = tbl
def get_flat(self, key):
s = self[key]
if isinstance(s, str): return s
return ' '.join(s)
def _get_list_value_for_modification(self, key):
"""Gets a value that must be a list for further modification. The
list may be modified inplace and there is no need to
"self.table[var] = value" afterwards.
"""
try:
value = self.table[key]
except KeyError:
try: value = self.parent[key]
except AttributeError: value = []
if isinstance(value, list):
value = value[:]
else:
value = [value]
else:
if not isinstance(value, list):
value = [value]
self.table[key] = value
return value
def append_value(self, var, value):
current_value = self._get_list_value_for_modification(var)
if isinstance(value, list):
current_value.extend(value)
else:
current_value.append(value)
def prepend_value(self, var, value):
current_value = self._get_list_value_for_modification(var)
if isinstance(value, list):
current_value = value + current_value
# a new list: update the dictionary entry
self.table[var] = current_value
else:
current_value.insert(0, value)
# prepend unique would be ambiguous
def append_unique(self, var, value):
current_value = self._get_list_value_for_modification(var)
if isinstance(value, list):
for value_item in value:
if value_item not in current_value:
current_value.append(value_item)
else:
if value not in current_value:
current_value.append(value)
def get_merged_dict(self):
"""compute a merged table"""
table_list = []
env = self
while 1:
table_list.insert(0, env.table)
try: env = env.parent
except AttributeError: break
merged_table = {}
for table in table_list:
merged_table.update(table)
return merged_table
def store(self, filename):
"Write the variables into a file"
file = open(filename, 'w')
merged_table = self.get_merged_dict()
keys = list(merged_table.keys())
keys.sort()
for k in keys: file.write('%s = %r\n' % (k, merged_table[k]))
file.close()
def load(self, filename):
"Retrieve the variables from a file"
tbl = self.table
code = Utils.readf(filename)
for m in re_imp.finditer(code):
g = m.group
tbl[g(2)] = eval(g(3))
Logs.debug('env: %s', self.table)
def get_destdir(self):
"return the destdir, useful for installing"
if self.__getitem__('NOINSTALL'): return ''
return Options.options.destdir
def update(self, d):
for k, v in d.iteritems():
self[k] = v
def __getattr__(self, name):
if name in self.__slots__:
return object.__getattr__(self, name)
else:
return self[name]
def __setattr__(self, name, value):
if name in self.__slots__:
object.__setattr__(self, name, value)
else:
self[name] = value
def __delattr__(self, name):
if name in self.__slots__:
object.__delattr__(self, name)
else:
del self[name]
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2005 (ita)
import ansiterm
import os, re, logging, traceback, sys
from Constants import *
zones = ''
verbose = 0
colors_lst = {
'USE' : True,
'BOLD' :'\x1b[01;1m',
'RED' :'\x1b[01;31m',
'GREEN' :'\x1b[32m',
'YELLOW':'\x1b[33m',
'PINK' :'\x1b[35m',
'BLUE' :'\x1b[01;34m',
'CYAN' :'\x1b[36m',
'NORMAL':'\x1b[0m',
'cursor_on' :'\x1b[?25h',
'cursor_off' :'\x1b[?25l',
}
got_tty = False
term = os.environ.get('TERM', 'dumb')
if not term in ['dumb', 'emacs']:
try:
got_tty = sys.stderr.isatty() or (sys.platform == 'win32' and term in ['xterm', 'msys'])
except AttributeError:
pass
import Utils
if not got_tty or 'NOCOLOR' in os.environ:
colors_lst['USE'] = False
# test
#if sys.platform == 'win32':
# colors_lst['USE'] = True
def get_color(cl):
if not colors_lst['USE']: return ''
return colors_lst.get(cl, '')
class foo(object):
def __getattr__(self, a):
return get_color(a)
def __call__(self, a):
return get_color(a)
colors = foo()
re_log = re.compile(r'(\w+): (.*)', re.M)
class log_filter(logging.Filter):
def __init__(self, name=None):
pass
def filter(self, rec):
rec.c1 = colors.PINK
rec.c2 = colors.NORMAL
rec.zone = rec.module
if rec.levelno >= logging.INFO:
if rec.levelno >= logging.ERROR:
rec.c1 = colors.RED
elif rec.levelno >= logging.WARNING:
rec.c1 = colors.YELLOW
else:
rec.c1 = colors.GREEN
return True
zone = ''
m = re_log.match(rec.msg)
if m:
zone = rec.zone = m.group(1)
rec.msg = m.group(2)
if zones:
return getattr(rec, 'zone', '') in zones or '*' in zones
elif not verbose > 2:
return False
return True
class formatter(logging.Formatter):
def __init__(self):
logging.Formatter.__init__(self, LOG_FORMAT, HOUR_FORMAT)
def format(self, rec):
if rec.levelno >= logging.WARNING or rec.levelno == logging.INFO:
try:
return '%s%s%s' % (rec.c1, rec.msg.decode('utf-8'), rec.c2)
except:
return rec.c1+rec.msg+rec.c2
return logging.Formatter.format(self, rec)
def debug(*k, **kw):
if verbose:
k = list(k)
k[0] = k[0].replace('\n', ' ')
logging.debug(*k, **kw)
def error(*k, **kw):
logging.error(*k, **kw)
if verbose > 1:
if isinstance(k[0], Utils.WafError):
st = k[0].stack
else:
st = traceback.extract_stack()
if st:
st = st[:-1]
buf = []
for filename, lineno, name, line in st:
buf.append(' File "%s", line %d, in %s' % (filename, lineno, name))
if line:
buf.append(' %s' % line.strip())
if buf: logging.error("\n".join(buf))
warn = logging.warn
info = logging.info
def init_log():
log = logging.getLogger()
log.handlers = []
log.filters = []
hdlr = logging.StreamHandler()
hdlr.setFormatter(formatter())
log.addHandler(hdlr)
log.addFilter(log_filter())
log.setLevel(logging.DEBUG)
# may be initialized more than once
init_log()
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2005 (ita)
"""
Node: filesystem structure, contains lists of nodes
IMPORTANT:
1. Each file/folder is represented by exactly one node.
2. Most would-be class properties are stored in Build: nodes to depend on, signature, flags, ..
unused class members increase the .wafpickle file size sensibly with lots of objects.
3. The build is launched from the top of the build dir (for example, in _build_/).
4. Node should not be instantiated directly.
Each instance of Build.BuildContext has a Node subclass.
(aka: 'Nodu', see BuildContext initializer)
The BuildContext is referenced here as self.__class__.bld
Its Node class is referenced here as self.__class__
The public and advertised apis are the following:
${TGT} -> dir/to/file.ext
${TGT[0].base()} -> dir/to/file
${TGT[0].dir(env)} -> dir/to
${TGT[0].file()} -> file.ext
${TGT[0].file_base()} -> file
${TGT[0].suffix()} -> .ext
${TGT[0].abspath(env)} -> /path/to/dir/to/file.ext
"""
import os, sys, fnmatch, re, stat
import Utils, Constants
UNDEFINED = 0
DIR = 1
FILE = 2
BUILD = 3
type_to_string = {UNDEFINED: "unk", DIR: "dir", FILE: "src", BUILD: "bld"}
# These fnmatch expressions are used by default to prune the directory tree
# while doing the recursive traversal in the find_iter method of the Node class.
prune_pats = '.git .bzr .hg .svn _MTN _darcs CVS SCCS'.split()
# These fnmatch expressions are used by default to exclude files and dirs
# while doing the recursive traversal in the find_iter method of the Node class.
exclude_pats = prune_pats + '*~ #*# .#* %*% ._* .gitignore .cvsignore vssver.scc .DS_Store'.split()
# These Utils.jar_regexp expressions are used by default to exclude files and dirs and also prune the directory tree
# while doing the recursive traversal in the ant_glob method of the Node class.
exclude_regs = '''
**/*~
**/#*#
**/.#*
**/%*%
**/._*
**/CVS
**/CVS/**
**/.cvsignore
**/SCCS
**/SCCS/**
**/vssver.scc
**/.svn
**/.svn/**
**/.git
**/.git/**
**/.gitignore
**/.bzr
**/.bzr/**
**/.hg
**/.hg/**
**/_MTN
**/_MTN/**
**/_darcs
**/_darcs/**
**/.DS_Store'''
class Node(object):
__slots__ = ("name", "parent", "id", "childs")
def __init__(self, name, parent, node_type = UNDEFINED):
self.name = name
self.parent = parent
# assumption: one build object at a time
self.__class__.bld.id_nodes += 4
self.id = self.__class__.bld.id_nodes + node_type
if node_type == DIR: self.childs = {}
# We do not want to add another type attribute (memory)
# use the id to find out: type = id & 3
# for setting: new type = type + x - type & 3
if parent and name in parent.childs:
raise Utils.WafError('node %s exists in the parent files %r already' % (name, parent))
if parent: parent.childs[name] = self
def __setstate__(self, data):
if len(data) == 4:
(self.parent, self.name, self.id, self.childs) = data
else:
(self.parent, self.name, self.id) = data
def __getstate__(self):
if getattr(self, 'childs', None) is None:
return (self.parent, self.name, self.id)
else:
return (self.parent, self.name, self.id, self.childs)
def __str__(self):
if not self.parent: return ''
return "%s://%s" % (type_to_string[self.id & 3], self.abspath())
def __repr__(self):
return self.__str__()
def __hash__(self):
"expensive, make certain it is not used"
raise Utils.WafError('nodes, you are doing it wrong')
def __copy__(self):
"nodes are not supposed to be copied"
raise Utils.WafError('nodes are not supposed to be cloned')
def get_type(self):
return self.id & 3
def set_type(self, t):
"dangerous, you are not supposed to use this"
self.id = self.id + t - self.id & 3
def dirs(self):
return [x for x in self.childs.values() if x.id & 3 == DIR]
def files(self):
return [x for x in self.childs.values() if x.id & 3 == FILE]
def get_dir(self, name, default=None):
node = self.childs.get(name, None)
if not node or node.id & 3 != DIR: return default
return node
def get_file(self, name, default=None):
node = self.childs.get(name, None)
if not node or node.id & 3 != FILE: return default
return node
def get_build(self, name, default=None):
node = self.childs.get(name, None)
if not node or node.id & 3 != BUILD: return default
return node
def find_resource(self, lst):
"Find an existing input file: either a build node declared previously or a source node"
if isinstance(lst, str):
lst = Utils.split_path(lst)
if len(lst) == 1:
parent = self
else:
parent = self.find_dir(lst[:-1])
if not parent: return None
self.__class__.bld.rescan(parent)
name = lst[-1]
node = parent.childs.get(name, None)
if node:
tp = node.id & 3
if tp == FILE or tp == BUILD:
return node
else:
return None
tree = self.__class__.bld
if not name in tree.cache_dir_contents[parent.id]:
return None
path = parent.abspath() + os.sep + name
try:
st = Utils.h_file(path)
except IOError:
return None
child = self.__class__(name, parent, FILE)
tree.node_sigs[0][child.id] = st
return child
def find_or_declare(self, lst):
"Used for declaring a build node representing a file being built"
if isinstance(lst, str):
lst = Utils.split_path(lst)
if len(lst) == 1:
parent = self
else:
parent = self.find_dir(lst[:-1])
if not parent: return None
self.__class__.bld.rescan(parent)
name = lst[-1]
node = parent.childs.get(name, None)
if node:
tp = node.id & 3
if tp != BUILD:
raise Utils.WafError('find_or_declare cannot return a build node (build files in the source directory %r?)' % lst)
return node
node = self.__class__(name, parent, BUILD)
return node
def find_dir(self, lst):
"search a folder in the filesystem"
if isinstance(lst, str):
lst = Utils.split_path(lst)
current = self
for name in lst:
self.__class__.bld.rescan(current)
prev = current
if not current.parent and name == current.name:
continue
elif not name:
continue
elif name == '.':
continue
elif name == '..':
current = current.parent or current
else:
current = prev.childs.get(name, None)
if current is None:
dir_cont = self.__class__.bld.cache_dir_contents
if prev.id in dir_cont and name in dir_cont[prev.id]:
if not prev.name:
if os.sep == '/':
# cygwin //machine/share
dirname = os.sep + name
else:
# windows c:
dirname = name
else:
# regular path
dirname = prev.abspath() + os.sep + name
if not os.path.isdir(dirname):
return None
current = self.__class__(name, prev, DIR)
elif (not prev.name and len(name) == 2 and name[1] == ':') or name.startswith('\\\\'):
# drive letter or \\ path for windows
current = self.__class__(name, prev, DIR)
else:
return None
else:
if current.id & 3 != DIR:
return None
return current
def ensure_dir_node_from_path(self, lst):
"used very rarely, force the construction of a branch of node instance for representing folders"
if isinstance(lst, str):
lst = Utils.split_path(lst)
current = self
for name in lst:
if not name:
continue
elif name == '.':
continue
elif name == '..':
current = current.parent or current
else:
prev = current
current = prev.childs.get(name, None)
if current is None:
current = self.__class__(name, prev, DIR)
return current
def exclusive_build_node(self, path):
"""
create a hierarchy in the build dir (no source folders) for ill-behaving compilers
the node is not hashed, so you must do it manually
after declaring such a node, find_dir and find_resource should work as expected
"""
lst = Utils.split_path(path)
name = lst[-1]
if len(lst) > 1:
parent = None
try:
parent = self.find_dir(lst[:-1])
except OSError:
pass
if not parent:
parent = self.ensure_dir_node_from_path(lst[:-1])
self.__class__.bld.rescan(parent)
else:
try:
self.__class__.bld.rescan(parent)
except OSError:
pass
else:
parent = self
node = parent.childs.get(name, None)
if not node:
node = self.__class__(name, parent, BUILD)
return node
def path_to_parent(self, parent):
"path relative to a direct ancestor, as string"
lst = []
p = self
h1 = parent.height()
h2 = p.height()
while h2 > h1:
h2 -= 1
lst.append(p.name)
p = p.parent
if lst:
lst.reverse()
ret = os.path.join(*lst)
else:
ret = ''
return ret
def find_ancestor(self, node):
"find a common ancestor for two nodes - for the shortest path in hierarchy"
dist = self.height() - node.height()
if dist < 0: return node.find_ancestor(self)
# now the real code
cand = self
while dist > 0:
cand = cand.parent
dist -= 1
if cand == node: return cand
cursor = node
while cand.parent:
cand = cand.parent
cursor = cursor.parent
if cand == cursor: return cand
def relpath_gen(self, from_node):
"string representing a relative path between self to another node"
if self == from_node: return '.'
if from_node.parent == self: return '..'
# up_path is '../../../' and down_path is 'dir/subdir/subdir/file'
ancestor = self.find_ancestor(from_node)
lst = []
cand = self
while not cand.id == ancestor.id:
lst.append(cand.name)
cand = cand.parent
cand = from_node
while not cand.id == ancestor.id:
lst.append('..')
cand = cand.parent
lst.reverse()
return os.sep.join(lst)
def nice_path(self, env=None):
"printed in the console, open files easily from the launch directory"
tree = self.__class__.bld
ln = tree.launch_node()
if self.id & 3 == FILE: return self.relpath_gen(ln)
else: return os.path.join(tree.bldnode.relpath_gen(ln), env.variant(), self.relpath_gen(tree.srcnode))
def is_child_of(self, node):
"does this node belong to the subtree node"
p = self
diff = self.height() - node.height()
while diff > 0:
diff -= 1
p = p.parent
return p.id == node.id
def variant(self, env):
"variant, or output directory for this node, a source has for variant 0"
if not env: return 0
elif self.id & 3 == FILE: return 0
else: return env.variant()
def height(self):
"amount of parents"
# README a cache can be added here if necessary
d = self
val = -1
while d:
d = d.parent
val += 1
return val
# helpers for building things
def abspath(self, env=None):
"""
absolute path
@param env [Environment]:
* obligatory for build nodes: build/variant/src/dir/bar.o
* optional for dirs: get either src/dir or build/variant/src/dir
* excluded for source nodes: src/dir/bar.c
Instead of computing the absolute path each time again,
store the already-computed absolute paths in one of (variants+1) dictionaries:
bld.cache_node_abspath[0] holds absolute paths for source nodes.
bld.cache_node_abspath[variant] holds the absolute path for the build nodes
which reside in the variant given by env.
"""
## absolute path - hot zone, so do not touch
# less expensive
variant = (env and (self.id & 3 != FILE) and env.variant()) or 0
ret = self.__class__.bld.cache_node_abspath[variant].get(self.id, None)
if ret: return ret
if not variant:
# source directory
if not self.parent:
val = os.sep == '/' and os.sep or ''
elif not self.parent.name: # root
val = (os.sep == '/' and os.sep or '') + self.name
else:
val = self.parent.abspath() + os.sep + self.name
else:
# build directory
val = os.sep.join((self.__class__.bld.bldnode.abspath(), variant, self.path_to_parent(self.__class__.bld.srcnode)))
self.__class__.bld.cache_node_abspath[variant][self.id] = val
return val
def change_ext(self, ext):
"node of the same path, but with a different extension - hot zone so do not touch"
name = self.name
k = name.rfind('.')
if k >= 0:
name = name[:k] + ext
else:
name = name + ext
return self.parent.find_or_declare([name])
def src_dir(self, env):
"src path without the file name"
return self.parent.srcpath(env)
def bld_dir(self, env):
"build path without the file name"
return self.parent.bldpath(env)
def bld_base(self, env):
"build path without the extension: src/dir/foo(.cpp)"
s = os.path.splitext(self.name)[0]
return os.path.join(self.bld_dir(env), s)
def bldpath(self, env=None):
"path seen from the build dir default/src/foo.cpp"
if self.id & 3 == FILE:
return self.relpath_gen(self.__class__.bld.bldnode)
p = self.path_to_parent(self.__class__.bld.srcnode)
if p is not '':
return env.variant() + os.sep + p
return env.variant()
def srcpath(self, env=None):
"path in the srcdir from the build dir ../src/foo.cpp"
if self.id & 3 == BUILD:
return self.bldpath(env)
return self.relpath_gen(self.__class__.bld.bldnode)
def read(self, env):
"get the contents of a file, it is not used anywhere for the moment"
return Utils.readf(self.abspath(env))
def dir(self, env):
"scons-like"
return self.parent.abspath(env)
def file(self):
"scons-like"
return self.name
def file_base(self):
"scons-like"
return os.path.splitext(self.name)[0]
def suffix(self):
"scons-like - hot zone so do not touch"
k = max(0, self.name.rfind('.'))
return self.name[k:]
def find_iter_impl(self, src=True, bld=True, dir=True, accept_name=None, is_prune=None, maxdepth=25):
"""find nodes in the filesystem hierarchy, try to instanciate the nodes passively; same gotcha as ant_glob"""
bld_ctx = self.__class__.bld
bld_ctx.rescan(self)
for name in bld_ctx.cache_dir_contents[self.id]:
if accept_name(self, name):
node = self.find_resource(name)
if node:
if src and node.id & 3 == FILE:
yield node
else:
node = self.find_dir(name)
if node and node.id != bld_ctx.bldnode.id:
if dir:
yield node
if not is_prune(self, name):
if maxdepth:
for k in node.find_iter_impl(src, bld, dir, accept_name, is_prune, maxdepth=maxdepth - 1):
yield k
else:
if not is_prune(self, name):
node = self.find_resource(name)
if not node:
# not a file, it is a dir
node = self.find_dir(name)
if node and node.id != bld_ctx.bldnode.id:
if maxdepth:
for k in node.find_iter_impl(src, bld, dir, accept_name, is_prune, maxdepth=maxdepth - 1):
yield k
if bld:
for node in self.childs.values():
if node.id == bld_ctx.bldnode.id:
continue
if node.id & 3 == BUILD:
if accept_name(self, node.name):
yield node
raise StopIteration
def find_iter(self, in_pat=['*'], ex_pat=exclude_pats, prune_pat=prune_pats, src=True, bld=True, dir=False, maxdepth=25, flat=False):
"""find nodes recursively, this returns everything but folders by default; same gotcha as ant_glob"""
if not (src or bld or dir):
raise StopIteration
if self.id & 3 != DIR:
raise StopIteration
in_pat = Utils.to_list(in_pat)
ex_pat = Utils.to_list(ex_pat)
prune_pat = Utils.to_list(prune_pat)
def accept_name(node, name):
for pat in ex_pat:
if fnmatch.fnmatchcase(name, pat):
return False
for pat in in_pat:
if fnmatch.fnmatchcase(name, pat):
return True
return False
def is_prune(node, name):
for pat in prune_pat:
if fnmatch.fnmatchcase(name, pat):
return True
return False
ret = self.find_iter_impl(src, bld, dir, accept_name, is_prune, maxdepth=maxdepth)
if flat:
return " ".join([x.relpath_gen(self) for x in ret])
return ret
def ant_glob(self, *k, **kw):
"""
known gotcha: will enumerate the files, but only if the folder exists in the source directory
"""
src=kw.get('src', 1)
bld=kw.get('bld', 0)
dir=kw.get('dir', 0)
excl = kw.get('excl', exclude_regs)
incl = k and k[0] or kw.get('incl', '**')
def to_pat(s):
lst = Utils.to_list(s)
ret = []
for x in lst:
x = x.replace('//', '/')
if x.endswith('/'):
x += '**'
lst2 = x.split('/')
accu = []
for k in lst2:
if k == '**':
accu.append(k)
else:
k = k.replace('.', '[.]').replace('*', '.*').replace('?', '.')
k = '^%s$' % k
#print "pattern", k
accu.append(re.compile(k))
ret.append(accu)
return ret
def filtre(name, nn):
ret = []
for lst in nn:
if not lst:
pass
elif lst[0] == '**':
ret.append(lst)
if len(lst) > 1:
if lst[1].match(name):
ret.append(lst[2:])
else:
ret.append([])
elif lst[0].match(name):
ret.append(lst[1:])
return ret
def accept(name, pats):
nacc = filtre(name, pats[0])
nrej = filtre(name, pats[1])
if [] in nrej:
nacc = []
return [nacc, nrej]
def ant_iter(nodi, maxdepth=25, pats=[]):
nodi.__class__.bld.rescan(nodi)
for name in nodi.__class__.bld.cache_dir_contents[nodi.id]:
npats = accept(name, pats)
if npats and npats[0]:
accepted = [] in npats[0]
#print accepted, nodi, name
node = nodi.find_resource(name)
if node and accepted:
if src and node.id & 3 == FILE:
yield node
else:
node = nodi.find_dir(name)
if node and node.id != nodi.__class__.bld.bldnode.id:
if accepted and dir:
yield node
if maxdepth:
for k in ant_iter(node, maxdepth=maxdepth - 1, pats=npats):
yield k
if bld:
for node in nodi.childs.values():
if node.id == nodi.__class__.bld.bldnode.id:
continue
if node.id & 3 == BUILD:
npats = accept(node.name, pats)
if npats and npats[0] and [] in npats[0]:
yield node
raise StopIteration
ret = [x for x in ant_iter(self, pats=[to_pat(incl), to_pat(excl)])]
if kw.get('flat', True):
return " ".join([x.relpath_gen(self) for x in ret])
return ret
def update_build_dir(self, env=None):
if not env:
for env in bld.all_envs:
self.update_build_dir(env)
return
path = self.abspath(env)
lst = Utils.listdir(path)
try:
self.__class__.bld.cache_dir_contents[self.id].update(lst)
except KeyError:
self.__class__.bld.cache_dir_contents[self.id] = set(lst)
self.__class__.bld.cache_scanned_folders[self.id] = True
for k in lst:
npath = path + os.sep + k
st = os.stat(npath)
if stat.S_ISREG(st[stat.ST_MODE]):
ick = self.find_or_declare(k)
if not (ick.id in self.__class__.bld.node_sigs[env.variant()]):
self.__class__.bld.node_sigs[env.variant()][ick.id] = Constants.SIG_NIL
elif stat.S_ISDIR(st[stat.ST_MODE]):
child = self.find_dir(k)
if not child:
child = self.ensure_dir_node_from_path(k)
child.update_build_dir(env)
class Nodu(Node):
pass
#!/usr/bin/env python
# encoding: utf-8
# Scott Newton, 2005 (scottn)
# Thomas Nagy, 2006 (ita)
"Custom command-line options"
import os, sys, imp, types, tempfile, optparse
import Logs, Utils
from Constants import *
cmds = 'distclean configure build install clean uninstall check dist distcheck'.split()
# TODO remove in waf 1.6 the following two
commands = {}
is_install = False
options = {}
arg_line = []
launch_dir = ''
tooldir = ''
lockfile = os.environ.get('WAFLOCK', '.lock-wscript')
try: cache_global = os.path.abspath(os.environ['WAFCACHE'])
except KeyError: cache_global = ''
platform = Utils.unversioned_sys_platform()
conf_file = 'conf-runs-%s-%d.pickle' % (platform, ABI)
remote_repo = ['http://waf.googlecode.com/svn/']
"""remote directory for the plugins"""
# Such a command-line should work: JOBS=4 PREFIX=/opt/ DESTDIR=/tmp/ahoj/ waf configure
default_prefix = os.environ.get('PREFIX')
if not default_prefix:
if platform == 'win32': default_prefix = tempfile.gettempdir()
else: default_prefix = '/usr/local/'
default_jobs = os.environ.get('JOBS', -1)
if default_jobs < 1:
try:
if 'SC_NPROCESSORS_ONLN' in os.sysconf_names:
default_jobs = os.sysconf('SC_NPROCESSORS_ONLN')
else:
default_jobs = int(Utils.cmd_output(['sysctl', '-n', 'hw.ncpu']))
except:
if os.name == 'java': # platform.system() == 'Java'
from java.lang import Runtime
default_jobs = Runtime.getRuntime().availableProcessors()
else:
# environment var defined on win32
default_jobs = int(os.environ.get('NUMBER_OF_PROCESSORS', 1))
default_destdir = os.environ.get('DESTDIR', '')
def get_usage(self):
cmds_str = []
module = Utils.g_module
if module:
# create the help messages for commands
tbl = module.__dict__
keys = list(tbl.keys())
keys.sort()
if 'build' in tbl:
if not module.build.__doc__:
module.build.__doc__ = 'builds the project'
if 'configure' in tbl:
if not module.configure.__doc__:
module.configure.__doc__ = 'configures the project'
ban = ['set_options', 'init', 'shutdown']
optlst = [x for x in keys if not x in ban
and type(tbl[x]) is type(parse_args_impl)
and tbl[x].__doc__
and not x.startswith('_')]
just = max([len(x) for x in optlst])
for x in optlst:
cmds_str.append(' %s: %s' % (x.ljust(just), tbl[x].__doc__))
ret = '\n'.join(cmds_str)
else:
ret = ' '.join(cmds)
return '''waf [command] [options]
Main commands (example: ./waf build -j4)
%s
''' % ret
setattr(optparse.OptionParser, 'get_usage', get_usage)
def create_parser(module=None):
Logs.debug('options: create_parser is called')
parser = optparse.OptionParser(conflict_handler="resolve", version = 'waf %s (%s)' % (WAFVERSION, WAFREVISION))
parser.formatter.width = Utils.get_term_cols()
p = parser.add_option
p('-j', '--jobs',
type = 'int',
default = default_jobs,
help = 'amount of parallel jobs (%r)' % default_jobs,
dest = 'jobs')
p('-k', '--keep',
action = 'store_true',
default = False,
help = 'keep running happily on independent task groups',
dest = 'keep')
p('-v', '--verbose',
action = 'count',
default = 0,
help = 'verbosity level -v -vv or -vvv [default: 0]',
dest = 'verbose')
p('--nocache',
action = 'store_true',
default = False,
help = 'ignore the WAFCACHE (if set)',
dest = 'nocache')
p('--zones',
action = 'store',
default = '',
help = 'debugging zones (task_gen, deps, tasks, etc)',
dest = 'zones')
p('-p', '--progress',
action = 'count',
default = 0,
help = '-p: progress bar; -pp: ide output',
dest = 'progress_bar')
p('--targets',
action = 'store',
default = '',
help = 'build given task generators, e.g. "target1,target2"',
dest = 'compile_targets')
gr = optparse.OptionGroup(parser, 'configuration options')
parser.add_option_group(gr)
gr.add_option('-b', '--blddir',
action = 'store',
default = '',
help = 'build dir for the project (configuration)',
dest = 'blddir')
gr.add_option('-s', '--srcdir',
action = 'store',
default = '',
help = 'src dir for the project (configuration)',
dest = 'srcdir')
gr.add_option('--prefix',
help = 'installation prefix (configuration) [default: %r]' % default_prefix,
default = default_prefix,
dest = 'prefix')
gr = optparse.OptionGroup(parser, 'installation options')
parser.add_option_group(gr)
gr.add_option('--destdir',
help = 'installation root [default: %r]' % default_destdir,
default = default_destdir,
dest = 'destdir')
gr.add_option('-f', '--force',
action = 'store_true',
default = False,
help = 'force file installation',
dest = 'force')
return parser
def parse_args_impl(parser, _args=None):
global options, commands, arg_line
(options, args) = parser.parse_args(args=_args)
arg_line = args
#arg_line = args[:] # copy
# By default, 'waf' is equivalent to 'waf build'
commands = {}
for var in cmds: commands[var] = 0
if not args:
commands['build'] = 1
args.append('build')
# Parse the command arguments
for arg in args:
commands[arg] = True
# the check thing depends on the build
if 'check' in args:
idx = args.index('check')
try:
bidx = args.index('build')
if bidx > idx:
raise ValueError('build before check')
except ValueError, e:
args.insert(idx, 'build')
if args[0] != 'init':
args.insert(0, 'init')
# TODO -k => -j0
if options.keep: options.jobs = 1
if options.jobs < 1: options.jobs = 1
if 'install' in sys.argv or 'uninstall' in sys.argv:
# absolute path only if set
options.destdir = options.destdir and os.path.abspath(os.path.expanduser(options.destdir))
Logs.verbose = options.verbose
Logs.init_log()
if options.zones:
Logs.zones = options.zones.split(',')
if not Logs.verbose: Logs.verbose = 1
elif Logs.verbose > 0:
Logs.zones = ['runner']
if Logs.verbose > 2:
Logs.zones = ['*']
# TODO waf 1.6
# 1. rename the class to OptionsContext
# 2. instead of a class attribute, use a module (static 'parser')
# 3. parse_args_impl was made in times when we did not know about binding new methods to classes
class Handler(Utils.Context):
"""loads wscript modules in folders for adding options
This class should be named 'OptionsContext'
A method named 'recurse' is bound when used by the module Scripting"""
parser = None
# make it possible to access the reference, like Build.bld
def __init__(self, module=None):
self.parser = create_parser(module)
self.cwd = os.getcwd()
Handler.parser = self
def add_option(self, *k, **kw):
self.parser.add_option(*k, **kw)
def add_option_group(self, *k, **kw):
return self.parser.add_option_group(*k, **kw)
def get_option_group(self, opt_str):
return self.parser.get_option_group(opt_str)
def sub_options(self, *k, **kw):
if not k: raise Utils.WscriptError('folder expected')
self.recurse(k[0], name='set_options')
def tool_options(self, *k, **kw):
Utils.python_24_guard()
if not k[0]:
raise Utils.WscriptError('invalid tool_options call %r %r' % (k, kw))
tools = Utils.to_list(k[0])
# TODO waf 1.6 remove the global variable tooldir
path = Utils.to_list(kw.get('tdir', kw.get('tooldir', tooldir)))
for tool in tools:
tool = tool.replace('++', 'xx')
if tool == 'java': tool = 'javaw'
if tool.lower() == 'unittest': tool = 'unittestw'
module = Utils.load_tool(tool, path)
try:
fun = module.set_options
except AttributeError:
pass
else:
fun(kw.get('option_group', self))
def parse_args(self, args=None):
parse_args_impl(self.parser, args)
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2005-2008 (ita)
"Execute the tasks"
import sys, random, time, threading, traceback
try: from Queue import Queue
except ImportError: from queue import Queue
import Build, Utils, Logs, Options
from Logs import debug, error
from Constants import *
GAP = 15
run_old = threading.Thread.run
def run(*args, **kwargs):
try:
run_old(*args, **kwargs)
except (KeyboardInterrupt, SystemExit):
raise
except:
sys.excepthook(*sys.exc_info())
threading.Thread.run = run
class TaskConsumer(threading.Thread):
ready = Queue(0)
consumers = []
def __init__(self):
threading.Thread.__init__(self)
self.setDaemon(1)
self.start()
def run(self):
try:
self.loop()
except:
pass
def loop(self):
while 1:
tsk = TaskConsumer.ready.get()
m = tsk.master
if m.stop:
m.out.put(tsk)
continue
try:
tsk.generator.bld.printout(tsk.display())
if tsk.__class__.stat: ret = tsk.__class__.stat(tsk)
# actual call to task's run() function
else: ret = tsk.call_run()
except Exception, e:
tsk.err_msg = Utils.ex_stack()
tsk.hasrun = EXCEPTION
# TODO cleanup
m.error_handler(tsk)
m.out.put(tsk)
continue
if ret:
tsk.err_code = ret
tsk.hasrun = CRASHED
else:
try:
tsk.post_run()
except Utils.WafError:
pass
except Exception:
tsk.err_msg = Utils.ex_stack()
tsk.hasrun = EXCEPTION
else:
tsk.hasrun = SUCCESS
if tsk.hasrun != SUCCESS:
m.error_handler(tsk)
m.out.put(tsk)
class Parallel(object):
"""
keep the consumer threads busy, and avoid consuming cpu cycles
when no more tasks can be added (end of the build, etc)
"""
def __init__(self, bld, j=2):
# number of consumers
self.numjobs = j
self.manager = bld.task_manager
self.manager.current_group = 0
self.total = self.manager.total()
# tasks waiting to be processed - IMPORTANT
self.outstanding = []
self.maxjobs = MAXJOBS
# tasks that are awaiting for another task to complete
self.frozen = []
# tasks returned by the consumers
self.out = Queue(0)
self.count = 0 # tasks not in the producer area
self.processed = 1 # progress indicator
self.stop = False # error condition to stop the build
self.error = False # error flag
def get_next(self):
"override this method to schedule the tasks in a particular order"
if not self.outstanding:
return None
return self.outstanding.pop(0)
def postpone(self, tsk):
"override this method to schedule the tasks in a particular order"
# TODO consider using a deque instead
if random.randint(0, 1):
self.frozen.insert(0, tsk)
else:
self.frozen.append(tsk)
def refill_task_list(self):
"called to set the next group of tasks"
while self.count > self.numjobs + GAP or self.count >= self.maxjobs:
self.get_out()
while not self.outstanding:
if self.count:
self.get_out()
if self.frozen:
self.outstanding += self.frozen
self.frozen = []
elif not self.count:
(jobs, tmp) = self.manager.get_next_set()
if jobs != None: self.maxjobs = jobs
if tmp: self.outstanding += tmp
break
def get_out(self):
"the tasks that are put to execute are all collected using get_out"
ret = self.out.get()
self.manager.add_finished(ret)
if not self.stop and getattr(ret, 'more_tasks', None):
self.outstanding += ret.more_tasks
self.total += len(ret.more_tasks)
self.count -= 1
def error_handler(self, tsk):
"by default, errors make the build stop (not thread safe so be careful)"
if not Options.options.keep:
self.stop = True
self.error = True
def start(self):
"execute the tasks"
if TaskConsumer.consumers:
# the worker pool is usually loaded lazily (see below)
# in case it is re-used with a different value of numjobs:
while len(TaskConsumer.consumers) < self.numjobs:
TaskConsumer.consumers.append(TaskConsumer())
while not self.stop:
self.refill_task_list()
# consider the next task
tsk = self.get_next()
if not tsk:
if self.count:
# tasks may add new ones after they are run
continue
else:
# no tasks to run, no tasks running, time to exit
break
if tsk.hasrun:
# if the task is marked as "run", just skip it
self.processed += 1
self.manager.add_finished(tsk)
continue
try:
st = tsk.runnable_status()
except Exception, e:
self.processed += 1
if self.stop and not Options.options.keep:
tsk.hasrun = SKIPPED
self.manager.add_finished(tsk)
continue
self.error_handler(tsk)
self.manager.add_finished(tsk)
tsk.hasrun = EXCEPTION
tsk.err_msg = Utils.ex_stack()
continue
if st == ASK_LATER:
self.postpone(tsk)
elif st == SKIP_ME:
self.processed += 1
tsk.hasrun = SKIPPED
self.manager.add_finished(tsk)
else:
# run me: put the task in ready queue
tsk.position = (self.processed, self.total)
self.count += 1
tsk.master = self
TaskConsumer.ready.put(tsk)
self.processed += 1
# create the consumer threads only if there is something to consume
if not TaskConsumer.consumers:
TaskConsumer.consumers = [TaskConsumer() for i in xrange(self.numjobs)]
# self.count represents the tasks that have been made available to the consumer threads
# collect all the tasks after an error else the message may be incomplete
while self.error and self.count:
self.get_out()
#print loop
assert (self.count == 0 or self.stop)
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2005 (ita)
"Module called for configuring, compiling and installing targets"
import os, sys, shutil, traceback, datetime, inspect, errno
import Utils, Configure, Build, Logs, Options, Environment, Task
from Logs import error, warn, info
from Constants import *
g_gz = 'bz2'
commands = []
def prepare_impl(t, cwd, ver, wafdir):
Options.tooldir = [t]
Options.launch_dir = cwd
# some command-line options can be processed immediately
if '--version' in sys.argv:
opt_obj = Options.Handler()
opt_obj.curdir = cwd
opt_obj.parse_args()
sys.exit(0)
# now find the wscript file
msg1 = 'Waf: Please run waf from a directory containing a file named "%s" or run distclean' % WSCRIPT_FILE
# in theory projects can be configured in an autotool-like manner:
# mkdir build && cd build && ../waf configure && ../waf
build_dir_override = None
candidate = None
lst = os.listdir(cwd)
search_for_candidate = True
if WSCRIPT_FILE in lst:
candidate = cwd
elif 'configure' in sys.argv and not WSCRIPT_BUILD_FILE in lst:
# autotool-like configuration
calldir = os.path.abspath(os.path.dirname(sys.argv[0]))
if WSCRIPT_FILE in os.listdir(calldir):
candidate = calldir
search_for_candidate = False
else:
error('arg[0] directory does not contain a wscript file')
sys.exit(1)
build_dir_override = cwd
# climb up to find a script if it is not found
while search_for_candidate:
if len(cwd) <= 3:
break # stop at / or c:
dirlst = os.listdir(cwd)
if WSCRIPT_FILE in dirlst:
candidate = cwd
if 'configure' in sys.argv and candidate:
break
if Options.lockfile in dirlst:
env = Environment.Environment()
try:
env.load(os.path.join(cwd, Options.lockfile))
except:
error('could not load %r' % Options.lockfile)
try:
os.stat(env['cwd'])
except:
candidate = cwd
else:
candidate = env['cwd']
break
cwd = os.path.dirname(cwd) # climb up
if not candidate:
# check if the user only wanted to display the help
if '-h' in sys.argv or '--help' in sys.argv:
warn('No wscript file found: the help message may be incomplete')
opt_obj = Options.Handler()
opt_obj.curdir = cwd
opt_obj.parse_args()
else:
error(msg1)
sys.exit(0)
# We have found wscript, but there is no guarantee that it is valid
try:
os.chdir(candidate)
except OSError:
raise Utils.WafError("the folder %r is unreadable" % candidate)
# define the main module containing the functions init, shutdown, ..
Utils.set_main_module(os.path.join(candidate, WSCRIPT_FILE))
if build_dir_override:
d = getattr(Utils.g_module, BLDDIR, None)
if d:
# test if user has set the blddir in wscript.
msg = ' Overriding build directory %s with %s' % (d, build_dir_override)
warn(msg)
Utils.g_module.blddir = build_dir_override
# bind a few methods and classes by default
def set_def(obj, name=''):
n = name or obj.__name__
if not n in Utils.g_module.__dict__:
setattr(Utils.g_module, n, obj)
for k in [dist, distclean, distcheck, clean, install, uninstall]:
set_def(k)
set_def(Configure.ConfigurationContext, 'configure_context')
for k in ['build', 'clean', 'install', 'uninstall']:
set_def(Build.BuildContext, k + '_context')
# now parse the options from the user wscript file
opt_obj = Options.Handler(Utils.g_module)
opt_obj.curdir = candidate
try:
f = Utils.g_module.set_options
except AttributeError:
pass
else:
opt_obj.sub_options([''])
opt_obj.parse_args()
if not 'init' in Utils.g_module.__dict__:
Utils.g_module.init = Utils.nada
if not 'shutdown' in Utils.g_module.__dict__:
Utils.g_module.shutdown = Utils.nada
main()
def prepare(t, cwd, ver, wafdir):
if WAFVERSION != ver:
msg = 'Version mismatch: waf %s <> wafadmin %s (wafdir %s)' % (ver, WAFVERSION, wafdir)
print('\033[91mError: %s\033[0m' % msg)
sys.exit(1)
#"""
try:
prepare_impl(t, cwd, ver, wafdir)
except Utils.WafError, e:
error(str(e))
sys.exit(1)
except KeyboardInterrupt:
Utils.pprint('RED', 'Interrupted')
sys.exit(68)
"""
import cProfile, pstats
cProfile.runctx("import Scripting; Scripting.prepare_impl(t, cwd, ver, wafdir)", {},
{'t': t, 'cwd':cwd, 'ver':ver, 'wafdir':wafdir},
'profi.txt')
p = pstats.Stats('profi.txt')
p.sort_stats('time').print_stats(45)
#"""
def main():
global commands
commands = Options.arg_line[:]
while commands:
x = commands.pop(0)
ini = datetime.datetime.now()
if x == 'configure':
fun = configure
elif x == 'build':
fun = build
else:
fun = getattr(Utils.g_module, x, None)
if not fun:
raise Utils.WscriptError('No such command %r' % x)
ctx = getattr(Utils.g_module, x + '_context', Utils.Context)()
if x in ['init', 'shutdown', 'dist', 'distclean', 'distcheck']:
# compatibility TODO remove in waf 1.6
try:
fun(ctx)
except TypeError:
fun()
else:
fun(ctx)
ela = ''
if not Options.options.progress_bar:
ela = ' (%s)' % Utils.get_elapsed_time(ini)
if x != 'init' and x != 'shutdown':
info('%r finished successfully%s' % (x, ela))
if not commands and x != 'shutdown':
commands.append('shutdown')
def configure(conf):
src = getattr(Options.options, SRCDIR, None)
if not src: src = getattr(Utils.g_module, SRCDIR, None)
if not src: src = getattr(Utils.g_module, 'top', None)
if not src:
src = '.'
incomplete_src = 1
src = os.path.abspath(src)
bld = getattr(Options.options, BLDDIR, None)
if not bld: bld = getattr(Utils.g_module, BLDDIR, None)
if not bld: bld = getattr(Utils.g_module, 'out', None)
if not bld:
bld = 'build'
incomplete_bld = 1
if bld == '.':
raise Utils.WafError('Setting blddir="." may cause distclean problems')
bld = os.path.abspath(bld)
try: os.makedirs(bld)
except OSError: pass
# It is not possible to compile specific targets in the configuration
# this may cause configuration errors if autoconfig is set
targets = Options.options.compile_targets
Options.options.compile_targets = None
Options.is_install = False
conf.srcdir = src
conf.blddir = bld
conf.post_init()
if 'incomplete_src' in vars():
conf.check_message_1('Setting srcdir to')
conf.check_message_2(src)
if 'incomplete_bld' in vars():
conf.check_message_1('Setting blddir to')
conf.check_message_2(bld)
# calling to main wscript's configure()
conf.sub_config([''])
conf.store()
# this will write a configure lock so that subsequent builds will
# consider the current path as the root directory (see prepare_impl).
# to remove: use 'waf distclean'
env = Environment.Environment()
env[BLDDIR] = bld
env[SRCDIR] = src
env['argv'] = sys.argv
env['commands'] = Options.commands
env['options'] = Options.options.__dict__
# conf.hash & conf.files hold wscript files paths and hash
# (used only by Configure.autoconfig)
env['hash'] = conf.hash
env['files'] = conf.files
env['environ'] = dict(conf.environ)
env['cwd'] = os.path.split(Utils.g_module.root_path)[0]
if Utils.g_module.root_path != src:
# in case the source dir is somewhere else
env.store(os.path.join(src, Options.lockfile))
env.store(Options.lockfile)
Options.options.compile_targets = targets
def clean(bld):
'''removes the build files'''
try:
proj = Environment.Environment(Options.lockfile)
except IOError:
raise Utils.WafError('Nothing to clean (project not configured)')
bld.load_dirs(proj[SRCDIR], proj[BLDDIR])
bld.load_envs()
bld.is_install = 0 # False
# read the scripts - and set the path to the wscript path (useful for srcdir='/foo/bar')
bld.add_subdirs([os.path.split(Utils.g_module.root_path)[0]])
try:
bld.clean()
finally:
bld.save()
def check_configured(bld):
if not Configure.autoconfig:
return bld
conf_cls = getattr(Utils.g_module, 'configure_context', Utils.Context)
bld_cls = getattr(Utils.g_module, 'build_context', Utils.Context)
def reconf(proj):
back = (Options.commands, Options.options.__dict__, Logs.zones, Logs.verbose)
Options.commands = proj['commands']
Options.options.__dict__ = proj['options']
conf = conf_cls()
conf.environ = proj['environ']
configure(conf)
(Options.commands, Options.options.__dict__, Logs.zones, Logs.verbose) = back
try:
proj = Environment.Environment(Options.lockfile)
except IOError:
conf = conf_cls()
configure(conf)
else:
try:
bld = bld_cls()
bld.load_dirs(proj[SRCDIR], proj[BLDDIR])
bld.load_envs()
except Utils.WafError:
reconf(proj)
return bld_cls()
try:
proj = Environment.Environment(Options.lockfile)
except IOError:
raise Utils.WafError('Auto-config: project does not configure (bug)')
h = 0
try:
for file in proj['files']:
if file.endswith('configure'):
h = hash((h, Utils.readf(file)))
else:
mod = Utils.load_module(file)
h = hash((h, mod.waf_hash_val))
except (OSError, IOError):
warn('Reconfiguring the project: a file is unavailable')
reconf(proj)
else:
if (h != proj['hash']):
warn('Reconfiguring the project: the configuration has changed')
reconf(proj)
return bld_cls()
def install(bld):
'''installs the build files'''
bld = check_configured(bld)
Options.commands['install'] = True
Options.commands['uninstall'] = False
Options.is_install = True
bld.is_install = INSTALL
build_impl(bld)
bld.install()
def uninstall(bld):
'''removes the installed files'''
Options.commands['install'] = False
Options.commands['uninstall'] = True
Options.is_install = True
bld.is_install = UNINSTALL
try:
def runnable_status(self):
return SKIP_ME
setattr(Task.Task, 'runnable_status_back', Task.Task.runnable_status)
setattr(Task.Task, 'runnable_status', runnable_status)
build_impl(bld)
bld.install()
finally:
setattr(Task.Task, 'runnable_status', Task.Task.runnable_status_back)
def build(bld):
bld = check_configured(bld)
Options.commands['install'] = False
Options.commands['uninstall'] = False
Options.is_install = False
bld.is_install = 0 # False
return build_impl(bld)
def build_impl(bld):
# compile the project and/or install the files
try:
proj = Environment.Environment(Options.lockfile)
except IOError:
raise Utils.WafError("Project not configured (run 'waf configure' first)")
bld.load_dirs(proj[SRCDIR], proj[BLDDIR])
bld.load_envs()
info("Waf: Entering directory `%s'" % bld.bldnode.abspath())
bld.add_subdirs([os.path.split(Utils.g_module.root_path)[0]])
# execute something immediately before the build starts
bld.pre_build()
try:
bld.compile()
finally:
if Options.options.progress_bar: print('')
info("Waf: Leaving directory `%s'" % bld.bldnode.abspath())
# execute something immediately after a successful build
bld.post_build()
bld.install()
excludes = '.bzr .bzrignore .git .gitignore .svn CVS .cvsignore .arch-ids {arch} SCCS BitKeeper .hg _MTN _darcs Makefile Makefile.in config.log .gitattributes .hgignore .hgtags'.split()
dist_exts = '~ .rej .orig .pyc .pyo .bak .tar.bz2 tar.gz .zip .swp'.split()
def dont_dist(name, src, build_dir):
global excludes, dist_exts
if (name.startswith(',,')
or name.startswith('++')
or name.startswith('.waf')
or (src == '.' and name == Options.lockfile)
or name in excludes
or name == build_dir
):
return True
for ext in dist_exts:
if name.endswith(ext):
return True
return False
# like shutil.copytree
# exclude files and to raise exceptions immediately
def copytree(src, dst, build_dir):
names = os.listdir(src)
os.makedirs(dst)
for name in names:
srcname = os.path.join(src, name)
dstname = os.path.join(dst, name)
if dont_dist(name, src, build_dir):
continue
if os.path.isdir(srcname):
copytree(srcname, dstname, build_dir)
else:
shutil.copy2(srcname, dstname)
# TODO in waf 1.6, change this method if "srcdir == blddir" is allowed
def distclean(ctx=None):
'''removes the build directory'''
global commands
lst = os.listdir('.')
for f in lst:
if f == Options.lockfile:
try:
proj = Environment.Environment(f)
except:
Logs.warn('could not read %r' % f)
continue
try:
shutil.rmtree(proj[BLDDIR])
except IOError:
pass
except OSError, e:
if e.errno != errno.ENOENT:
Logs.warn('project %r cannot be removed' % proj[BLDDIR])
try:
os.remove(f)
except OSError, e:
if e.errno != errno.ENOENT:
Logs.warn('file %r cannot be removed' % f)
# remove the local waf cache
if not commands and f.startswith('.waf'):
shutil.rmtree(f, ignore_errors=True)
# FIXME waf 1.6 a unique ctx parameter, and remove the optional appname and version
def dist(appname='', version=''):
'''makes a tarball for redistributing the sources'''
# return return (distdirname, tarballname)
import tarfile
if not appname: appname = Utils.g_module.APPNAME
if not version: version = Utils.g_module.VERSION
tmp_folder = appname + '-' + version
if g_gz in ['gz', 'bz2']:
arch_name = tmp_folder + '.tar.' + g_gz
else:
arch_name = tmp_folder + '.' + 'zip'
# remove the previous dir
try:
shutil.rmtree(tmp_folder)
except (OSError, IOError):
pass
# remove the previous archive
try:
os.remove(arch_name)
except (OSError, IOError):
pass
# copy the files into the temporary folder
blddir = getattr(Utils.g_module, BLDDIR, None)
if not blddir:
blddir = getattr(Utils.g_module, 'out', None)
copytree('.', tmp_folder, blddir)
# undocumented hook for additional cleanup
dist_hook = getattr(Utils.g_module, 'dist_hook', None)
if dist_hook:
back = os.getcwd()
os.chdir(tmp_folder)
try:
dist_hook()
finally:
# go back to the root directory
os.chdir(back)
if g_gz in ['gz', 'bz2']:
tar = tarfile.open(arch_name, 'w:' + g_gz)
tar.add(tmp_folder)
tar.close()
else:
Utils.zip_folder(tmp_folder, arch_name, tmp_folder)
try: from hashlib import sha1 as sha
except ImportError: from sha import sha
try:
digest = " (sha=%r)" % sha(Utils.readf(arch_name)).hexdigest()
except:
digest = ''
info('New archive created: %s%s' % (arch_name, digest))
if os.path.exists(tmp_folder): shutil.rmtree(tmp_folder)
return arch_name
# FIXME waf 1.6 a unique ctx parameter, and remove the optional appname and version
def distcheck(appname='', version='', subdir=''):
'''checks if the sources compile (tarball from 'dist')'''
import tempfile, tarfile
if not appname: appname = Utils.g_module.APPNAME
if not version: version = Utils.g_module.VERSION
waf = os.path.abspath(sys.argv[0])
tarball = dist(appname, version)
path = appname + '-' + version
# remove any previous instance
if os.path.exists(path):
shutil.rmtree(path)
t = tarfile.open(tarball)
for x in t: t.extract(x)
t.close()
# build_path is the directory for the waf invocation
if subdir:
build_path = os.path.join(path, subdir)
else:
build_path = path
instdir = tempfile.mkdtemp('.inst', '%s-%s' % (appname, version))
ret = Utils.pproc.Popen([waf, 'configure', 'build', 'install', 'uninstall', '--destdir=' + instdir], cwd=build_path).wait()
if ret:
raise Utils.WafError('distcheck failed with code %i' % ret)
if os.path.exists(instdir):
raise Utils.WafError('distcheck succeeded, but files were left in %s' % instdir)
shutil.rmtree(path)
# FIXME remove in Waf 1.6 (kept for compatibility)
def add_subdir(dir, bld):
bld.recurse(dir, 'build')
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2005-2008 (ita)
"""
Running tasks in parallel is a simple problem, but in practice it is more complicated:
* dependencies discovered during the build (dynamic task creation)
* dependencies discovered after files are compiled
* the amount of tasks and dependencies (graph size) can be huge
This is why the dependency management is split on three different levels:
1. groups of tasks that run all after another group of tasks
2. groups of tasks that can be run in parallel
3. tasks that can run in parallel, but with possible unknown ad-hoc dependencies
The point #1 represents a strict sequential order between groups of tasks, for example a compiler is produced
and used to compile the rest, whereas #2 and #3 represent partial order constraints where #2 applies to the kind of task
and #3 applies to the task instances.
#1 is held by the task manager: ordered list of TaskGroups (see bld.add_group)
#2 is held by the task groups and the task types: precedence after/before (topological sort),
and the constraints extracted from file extensions
#3 is held by the tasks individually (attribute run_after),
and the scheduler (Runner.py) use Task::runnable_status to reorder the tasks
--
To try, use something like this in your code:
import Constants, Task
Task.algotype = Constants.MAXPARALLEL
--
There are two concepts with the tasks (individual units of change):
* dependency (if 1 is recompiled, recompile 2)
* order (run 2 after 1)
example 1: if t1 depends on t2 and t2 depends on t3 it is not necessary to make t1 depend on t3 (dependency is transitive)
example 2: if t1 depends on a node produced by t2, it is not immediately obvious that t1 must run after t2 (order is not obvious)
The role of the Task Manager is to give the tasks in order (groups of task that may be run in parallel one after the other)
"""
import os, shutil, sys, re, random, datetime, tempfile, shlex
from Utils import md5
import Build, Runner, Utils, Node, Logs, Options
from Logs import debug, warn, error
from Constants import *
algotype = NORMAL
#algotype = JOBCONTROL
#algotype = MAXPARALLEL
COMPILE_TEMPLATE_SHELL = '''
def f(task):
env = task.env
wd = getattr(task, 'cwd', None)
p = env.get_flat
cmd = \'\'\' %s \'\'\' % s
return task.exec_command(cmd, cwd=wd)
'''
COMPILE_TEMPLATE_NOSHELL = '''
def f(task):
env = task.env
wd = getattr(task, 'cwd', None)
def to_list(xx):
if isinstance(xx, str): return [xx]
return xx
lst = []
%s
lst = [x for x in lst if x]
return task.exec_command(lst, cwd=wd)
'''
"""
Enable different kind of dependency algorithms:
1 make groups: first compile all cpps and then compile all links (NORMAL)
2 parallelize all (each link task run after its dependencies) (MAXPARALLEL)
3 like 1 but provide additional constraints for the parallelization (MAXJOBS)
In theory 1. will be faster than 2 for waf, but might be slower for builds
The scheme 2 will not allow for running tasks one by one so it can cause disk thrashing on huge builds
"""
file_deps = Utils.nada
"""
Additional dependency pre-check may be added by replacing the function file_deps.
e.g. extract_outputs, extract_deps below.
"""
class TaskManager(object):
"""The manager is attached to the build object, it holds a list of TaskGroup"""
def __init__(self):
self.groups = []
self.tasks_done = []
self.current_group = 0
self.groups_names = {}
def get_next_set(self):
"""return the next set of tasks to execute
the first parameter is the maximum amount of parallelization that may occur"""
ret = None
while not ret and self.current_group < len(self.groups):
ret = self.groups[self.current_group].get_next_set()
if ret: return ret
else:
self.groups[self.current_group].process_install()
self.current_group += 1
return (None, None)
def add_group(self, name=None, set=True):
#if self.groups and not self.groups[0].tasks:
# error('add_group: an empty group is already present')
g = TaskGroup()
if name and name in self.groups_names:
error('add_group: name %s already present' % name)
self.groups_names[name] = g
self.groups.append(g)
if set:
self.current_group = len(self.groups) - 1
def set_group(self, idx):
if isinstance(idx, str):
g = self.groups_names[idx]
for x in xrange(len(self.groups)):
if id(g) == id(self.groups[x]):
self.current_group = x
else:
self.current_group = idx
def add_task_gen(self, tgen):
if not self.groups: self.add_group()
self.groups[self.current_group].tasks_gen.append(tgen)
def add_task(self, task):
if not self.groups: self.add_group()
self.groups[self.current_group].tasks.append(task)
def total(self):
total = 0
if not self.groups: return 0
for group in self.groups:
total += len(group.tasks)
return total
def add_finished(self, tsk):
self.tasks_done.append(tsk)
bld = tsk.generator.bld
if bld.is_install:
f = None
if 'install' in tsk.__dict__:
f = tsk.__dict__['install']
# install=0 to prevent installation
if f: f(tsk)
else:
tsk.install()
class TaskGroup(object):
"the compilation of one group does not begin until the previous group has finished (in the manager)"
def __init__(self):
self.tasks = [] # this list will be consumed
self.tasks_gen = []
self.cstr_groups = Utils.DefaultDict(list) # tasks having equivalent constraints
self.cstr_order = Utils.DefaultDict(set) # partial order between the cstr groups
self.temp_tasks = [] # tasks put on hold
self.ready = 0
self.post_funs = []
def reset(self):
"clears the state of the object (put back the tasks into self.tasks)"
for x in self.cstr_groups:
self.tasks += self.cstr_groups[x]
self.tasks = self.temp_tasks + self.tasks
self.temp_tasks = []
self.cstr_groups = Utils.DefaultDict(list)
self.cstr_order = Utils.DefaultDict(set)
self.ready = 0
def process_install(self):
for (f, k, kw) in self.post_funs:
f(*k, **kw)
def prepare(self):
"prepare the scheduling"
self.ready = 1
file_deps(self.tasks)
self.make_cstr_groups()
self.extract_constraints()
def get_next_set(self):
"next list of tasks to execute using max job settings, returns (maxjobs, task_list)"
global algotype
if algotype == NORMAL:
tasks = self.tasks_in_parallel()
maxj = MAXJOBS
elif algotype == JOBCONTROL:
(maxj, tasks) = self.tasks_by_max_jobs()
elif algotype == MAXPARALLEL:
tasks = self.tasks_with_inner_constraints()
maxj = MAXJOBS
else:
raise Utils.WafError("unknown algorithm type %s" % (algotype))
if not tasks: return ()
return (maxj, tasks)
def make_cstr_groups(self):
"unite the tasks that have similar constraints"
self.cstr_groups = Utils.DefaultDict(list)
for x in self.tasks:
h = x.hash_constraints()
self.cstr_groups[h].append(x)
def set_order(self, a, b):
self.cstr_order[a].add(b)
def compare_exts(self, t1, t2):
"extension production"
x = "ext_in"
y = "ext_out"
in_ = t1.attr(x, ())
out_ = t2.attr(y, ())
for k in in_:
if k in out_:
return -1
in_ = t2.attr(x, ())
out_ = t1.attr(y, ())
for k in in_:
if k in out_:
return 1
return 0
def compare_partial(self, t1, t2):
"partial relations after/before"
m = "after"
n = "before"
name = t2.__class__.__name__
if name in Utils.to_list(t1.attr(m, ())): return -1
elif name in Utils.to_list(t1.attr(n, ())): return 1
name = t1.__class__.__name__
if name in Utils.to_list(t2.attr(m, ())): return 1
elif name in Utils.to_list(t2.attr(n, ())): return -1
return 0
def extract_constraints(self):
"extract the parallelization constraints from the tasks with different constraints"
keys = self.cstr_groups.keys()
max = len(keys)
# hopefully the length of this list is short
for i in xrange(max):
t1 = self.cstr_groups[keys[i]][0]
for j in xrange(i + 1, max):
t2 = self.cstr_groups[keys[j]][0]
# add the constraints based on the comparisons
val = (self.compare_exts(t1, t2)
or self.compare_partial(t1, t2)
)
if val > 0:
self.set_order(keys[i], keys[j])
elif val < 0:
self.set_order(keys[j], keys[i])
def tasks_in_parallel(self):
"(NORMAL) next list of tasks that may be executed in parallel"
if not self.ready: self.prepare()
keys = self.cstr_groups.keys()
unconnected = []
remainder = []
for u in keys:
for k in self.cstr_order.values():
if u in k:
remainder.append(u)
break
else:
unconnected.append(u)
toreturn = []
for y in unconnected:
toreturn.extend(self.cstr_groups[y])
# remove stuff only after
for y in unconnected:
try: self.cstr_order.__delitem__(y)
except KeyError: pass
self.cstr_groups.__delitem__(y)
if not toreturn and remainder:
raise Utils.WafError("circular order constraint detected %r" % remainder)
return toreturn
def tasks_by_max_jobs(self):
"(JOBCONTROL) returns the tasks that can run in parallel with the max amount of jobs"
if not self.ready: self.prepare()
if not self.temp_tasks: self.temp_tasks = self.tasks_in_parallel()
if not self.temp_tasks: return (None, None)
maxjobs = MAXJOBS
ret = []
remaining = []
for t in self.temp_tasks:
m = getattr(t, "maxjobs", getattr(self.__class__, "maxjobs", MAXJOBS))
if m > maxjobs:
remaining.append(t)
elif m < maxjobs:
remaining += ret
ret = [t]
maxjobs = m
else:
ret.append(t)
self.temp_tasks = remaining
return (maxjobs, ret)
def tasks_with_inner_constraints(self):
"""(MAXPARALLEL) returns all tasks in this group, but add the constraints on each task instance
as an optimization, it might be desirable to discard the tasks which do not have to run"""
if not self.ready: self.prepare()
if getattr(self, "done", None): return None
for p in self.cstr_order:
for v in self.cstr_order[p]:
for m in self.cstr_groups[p]:
for n in self.cstr_groups[v]:
n.set_run_after(m)
self.cstr_order = Utils.DefaultDict(set)
self.cstr_groups = Utils.DefaultDict(list)
self.done = 1
return self.tasks[:] # make a copy
class store_task_type(type):
"store the task types that have a name ending in _task into a map (remember the existing task types)"
def __init__(cls, name, bases, dict):
super(store_task_type, cls).__init__(name, bases, dict)
name = cls.__name__
if name.endswith('_task'):
name = name.replace('_task', '')
if name != 'TaskBase':
TaskBase.classes[name] = cls
class TaskBase(object):
"""Base class for all Waf tasks
The most important methods are (by usual order of call):
1 runnable_status: ask the task if it should be run, skipped, or if we have to ask later
2 __str__: string to display to the user
3 run: execute the task
4 post_run: after the task is run, update the cache about the task
This class should be seen as an interface, it provides the very minimum necessary for the scheduler
so it does not do much.
For illustration purposes, TaskBase instances try to execute self.fun (if provided)
"""
__metaclass__ = store_task_type
color = "GREEN"
maxjobs = MAXJOBS
classes = {}
stat = None
def __init__(self, *k, **kw):
self.hasrun = NOT_RUN
try:
self.generator = kw['generator']
except KeyError:
self.generator = self
self.bld = Build.bld
if kw.get('normal', 1):
self.generator.bld.task_manager.add_task(self)
def __repr__(self):
"used for debugging"
return '\n\t{task: %s %s}' % (self.__class__.__name__, str(getattr(self, "fun", "")))
def __str__(self):
"string to display to the user"
if hasattr(self, 'fun'):
return 'executing: %s\n' % self.fun.__name__
return self.__class__.__name__ + '\n'
def exec_command(self, *k, **kw):
"use this for executing commands from tasks"
# TODO in waf 1.6, eliminate bld.exec_command, and move the cwd processing to here
if self.env['env']:
kw['env'] = self.env['env']
return self.generator.bld.exec_command(*k, **kw)
def runnable_status(self):
"RUN_ME SKIP_ME or ASK_LATER"
return RUN_ME
def can_retrieve_cache(self):
return False
def call_run(self):
if self.can_retrieve_cache():
return 0
return self.run()
def run(self):
"called if the task must run"
if hasattr(self, 'fun'):
return self.fun(self)
return 0
def post_run(self):
"update the dependency tree (node stats)"
pass
def display(self):
"print either the description (using __str__) or the progress bar or the ide output"
col1 = Logs.colors(self.color)
col2 = Logs.colors.NORMAL
if Options.options.progress_bar == 1:
return self.generator.bld.progress_line(self.position[0], self.position[1], col1, col2)
if Options.options.progress_bar == 2:
ela = Utils.get_elapsed_time(self.generator.bld.ini)
try:
ins = ','.join([n.name for n in self.inputs])
except AttributeError:
ins = ''
try:
outs = ','.join([n.name for n in self.outputs])
except AttributeError:
outs = ''
return '|Total %s|Current %s|Inputs %s|Outputs %s|Time %s|\n' % (self.position[1], self.position[0], ins, outs, ela)
total = self.position[1]
n = len(str(total))
fs = '[%%%dd/%%%dd] %%s%%s%%s' % (n, n)
return fs % (self.position[0], self.position[1], col1, str(self), col2)
def attr(self, att, default=None):
"retrieve an attribute from the instance or from the class (microoptimization here)"
ret = getattr(self, att, self)
if ret is self: return getattr(self.__class__, att, default)
return ret
def hash_constraints(self):
"identify a task type for all the constraints relevant for the scheduler: precedence, file production"
a = self.attr
sum = hash((self.__class__.__name__,
str(a('before', '')),
str(a('after', '')),
str(a('ext_in', '')),
str(a('ext_out', '')),
self.__class__.maxjobs))
return sum
def format_error(self):
"error message to display to the user (when a build fails)"
if getattr(self, "err_msg", None):
return self.err_msg
elif self.hasrun == CRASHED:
try:
return " -> task failed (err #%d): %r" % (self.err_code, self)
except AttributeError:
return " -> task failed: %r" % self
elif self.hasrun == MISSING:
return " -> missing files: %r" % self
else:
return ''
def install(self):
"""
installation is performed by looking at the task attributes:
* install_path: installation path like "${PREFIX}/bin"
* filename: install the first node in the outputs as a file with a particular name, be certain to give os.sep
* chmod: permissions
"""
bld = self.generator.bld
d = self.attr('install')
if self.attr('install_path'):
lst = [a.relpath_gen(bld.srcnode) for a in self.outputs]
perm = self.attr('chmod', O644)
if self.attr('src'):
# if src is given, install the sources too
lst += [a.relpath_gen(bld.srcnode) for a in self.inputs]
if self.attr('filename'):
dir = self.install_path.rstrip(os.sep) + os.sep + self.attr('filename')
bld.install_as(dir, lst[0], self.env, perm)
else:
bld.install_files(self.install_path, lst, self.env, perm)
class Task(TaskBase):
"""The parent class is quite limited, in this version:
* file system interaction: input and output nodes
* persistence: do not re-execute tasks that have already run
* caching: same files can be saved and retrieved from a cache directory
* dependencies:
implicit, like .c files depending on .h files
explicit, like the input nodes or the dep_nodes
environment variables, like the CXXFLAGS in self.env
"""
vars = []
def __init__(self, env, **kw):
TaskBase.__init__(self, **kw)
self.env = env
# inputs and outputs are nodes
# use setters when possible
self.inputs = []
self.outputs = []
self.deps_nodes = []
self.run_after = []
# Additionally, you may define the following
#self.dep_vars = 'PREFIX DATADIR'
def __str__(self):
"string to display to the user"
env = self.env
src_str = ' '.join([a.nice_path(env) for a in self.inputs])
tgt_str = ' '.join([a.nice_path(env) for a in self.outputs])
if self.outputs: sep = ' -> '
else: sep = ''
return '%s: %s%s%s\n' % (self.__class__.__name__.replace('_task', ''), src_str, sep, tgt_str)
def __repr__(self):
return "".join(['\n\t{task: ', self.__class__.__name__, " ", ",".join([x.name for x in self.inputs]), " -> ", ",".join([x.name for x in self.outputs]), '}'])
def unique_id(self):
"get a unique id: hash the node paths, the variant, the class, the function"
try:
return self.uid
except AttributeError:
"this is not a real hot zone, but we want to avoid surprizes here"
m = md5()
up = m.update
up(self.__class__.__name__)
up(self.env.variant())
p = None
for x in self.inputs + self.outputs:
if p != x.parent.id:
p = x.parent.id
up(x.parent.abspath())
up(x.name)
self.uid = m.digest()
return self.uid
def set_inputs(self, inp):
if isinstance(inp, list): self.inputs += inp
else: self.inputs.append(inp)
def set_outputs(self, out):
if isinstance(out, list): self.outputs += out
else: self.outputs.append(out)
def set_run_after(self, task):
"set (scheduler) order on another task"
# TODO: handle list or object
assert isinstance(task, TaskBase)
self.run_after.append(task)
def add_file_dependency(self, filename):
"TODO user-provided file dependencies"
node = self.generator.bld.path.find_resource(filename)
self.deps_nodes.append(node)
def signature(self):
# compute the result one time, and suppose the scan_signature will give the good result
try: return self.cache_sig[0]
except AttributeError: pass
self.m = md5()
# explicit deps
exp_sig = self.sig_explicit_deps()
# env vars
var_sig = self.sig_vars()
# implicit deps
imp_sig = SIG_NIL
if self.scan:
try:
imp_sig = self.sig_implicit_deps()
except ValueError:
return self.signature()
# we now have the signature (first element) and the details (for debugging)
ret = self.m.digest()
self.cache_sig = (ret, exp_sig, imp_sig, var_sig)
return ret
def runnable_status(self):
"SKIP_ME RUN_ME or ASK_LATER"
#return 0 # benchmarking
if self.inputs and (not self.outputs):
if not getattr(self.__class__, 'quiet', None):
warn("invalid task (no inputs OR outputs): override in a Task subclass or set the attribute 'quiet' %r" % self)
for t in self.run_after:
if not t.hasrun:
return ASK_LATER
env = self.env
bld = self.generator.bld
# first compute the signature
new_sig = self.signature()
# compare the signature to a signature computed previously
key = self.unique_id()
try:
prev_sig = bld.task_sigs[key][0]
except KeyError:
debug("task: task %r must run as it was never run before or the task code changed", self)
return RUN_ME
# compare the signatures of the outputs
for node in self.outputs:
variant = node.variant(env)
try:
if bld.node_sigs[variant][node.id] != new_sig:
return RUN_ME
except KeyError:
debug("task: task %r must run as the output nodes do not exist", self)
return RUN_ME
# debug if asked to
if Logs.verbose: self.debug_why(bld.task_sigs[key])
if new_sig != prev_sig:
return RUN_ME
return SKIP_ME
def post_run(self):
"called after a successful task run"
bld = self.generator.bld
env = self.env
sig = self.signature()
ssig = sig.encode('hex')
variant = env.variant()
for node in self.outputs:
# check if the node exists ..
try:
os.stat(node.abspath(env))
except OSError:
self.hasrun = MISSING
self.err_msg = '-> missing file: %r' % node.abspath(env)
raise Utils.WafError
# important, store the signature for the next run
bld.node_sigs[variant][node.id] = sig
bld.task_sigs[self.unique_id()] = self.cache_sig
# file caching, if possible
# try to avoid data corruption as much as possible
if not Options.cache_global or Options.options.nocache or not self.outputs:
return None
if getattr(self, 'cached', None):
return None
dname = os.path.join(Options.cache_global, ssig)
tmpdir = tempfile.mkdtemp(prefix=Options.cache_global)
try:
shutil.rmtree(dname)
except:
pass
try:
for node in self.outputs:
variant = node.variant(env)
dest = os.path.join(tmpdir, node.name)
shutil.copy2(node.abspath(env), dest)
except (OSError, IOError):
try:
shutil.rmtree(tmpdir)
except:
pass
else:
try:
os.rename(tmpdir, dname)
except OSError:
try:
shutil.rmtree(tmpdir)
except:
pass
else:
try:
os.chmod(dname, O755)
except:
pass
def can_retrieve_cache(self):
"""
Retrieve build nodes from the cache
update the file timestamps to help cleaning the least used entries from the cache
additionally, set an attribute 'cached' to avoid re-creating the same cache files
suppose there are files in cache/dir1/file1 and cache/dir2/file2
first, read the timestamp of dir1
then try to copy the files
then look at the timestamp again, if it has changed, the data may have been corrupt (cache update by another process)
should an exception occur, ignore the data
"""
if not Options.cache_global or Options.options.nocache or not self.outputs:
return None
env = self.env
sig = self.signature()
ssig = sig.encode('hex')
# first try to access the cache folder for the task
dname = os.path.join(Options.cache_global, ssig)
try:
t1 = os.stat(dname).st_mtime
except OSError:
return None
for node in self.outputs:
variant = node.variant(env)
orig = os.path.join(dname, node.name)
try:
shutil.copy2(orig, node.abspath(env))
# mark the cache file as used recently (modified)
os.utime(orig, None)
except (OSError, IOError):
debug('task: failed retrieving file')
return None
# is it the same folder?
try:
t2 = os.stat(dname).st_mtime
except OSError:
return None
if t1 != t2:
return None
for node in self.outputs:
self.generator.bld.node_sigs[variant][node.id] = sig
if Options.options.progress_bar < 1:
self.generator.bld.printout('restoring from cache %r\n' % node.bldpath(env))
self.cached = True
return 1
def debug_why(self, old_sigs):
"explains why a task is run"
new_sigs = self.cache_sig
def v(x):
return x.encode('hex')
debug("Task %r", self)
msgs = ['Task must run', '* Source file or manual dependency', '* Implicit dependency', '* Environment variable']
tmp = 'task: -> %s: %s %s'
for x in xrange(len(msgs)):
if (new_sigs[x] != old_sigs[x]):
debug(tmp, msgs[x], v(old_sigs[x]), v(new_sigs[x]))
def sig_explicit_deps(self):
bld = self.generator.bld
up = self.m.update
# the inputs
for x in self.inputs + getattr(self, 'dep_nodes', []):
if not x.parent.id in bld.cache_scanned_folders:
bld.rescan(x.parent)
variant = x.variant(self.env)
try:
up(bld.node_sigs[variant][x.id])
except KeyError:
raise Utils.WafError('Missing node signature for %r (required by %r)' % (x, self))
# manual dependencies, they can slow down the builds
if bld.deps_man:
additional_deps = bld.deps_man
for x in self.inputs + self.outputs:
try:
d = additional_deps[x.id]
except KeyError:
continue
for v in d:
if isinstance(v, Node.Node):
bld.rescan(v.parent)
variant = v.variant(self.env)
try:
v = bld.node_sigs[variant][v.id]
except KeyError:
raise Utils.WafError('Missing node signature for %r (required by %r)' % (v, self))
elif hasattr(v, '__call__'):
v = v() # dependency is a function, call it
up(v)
for x in self.deps_nodes:
v = bld.node_sigs[x.variant(self.env)][x.id]
up(v)
return self.m.digest()
def sig_vars(self):
bld = self.generator.bld
env = self.env
# dependencies on the environment vars
act_sig = bld.hash_env_vars(env, self.__class__.vars)
self.m.update(act_sig)
# additional variable dependencies, if provided
dep_vars = getattr(self, 'dep_vars', None)
if dep_vars:
self.m.update(bld.hash_env_vars(env, dep_vars))
return self.m.digest()
#def scan(self, node):
# """this method returns a tuple containing:
# * a list of nodes corresponding to real files
# * a list of names for files not found in path_lst
# the input parameters may have more parameters that the ones used below
# """
# return ((), ())
scan = None
# compute the signature, recompute it if there is no match in the cache
def sig_implicit_deps(self):
"the signature obtained may not be the one if the files have changed, we do it in two steps"
bld = self.generator.bld
# get the task signatures from previous runs
key = self.unique_id()
prev_sigs = bld.task_sigs.get(key, ())
if prev_sigs:
try:
# for issue #379
if prev_sigs[2] == self.compute_sig_implicit_deps():
return prev_sigs[2]
except (KeyError, OSError):
pass
del bld.task_sigs[key]
raise ValueError('rescan')
# no previous run or the signature of the dependencies has changed, rescan the dependencies
(nodes, names) = self.scan()
if Logs.verbose:
debug('deps: scanner for %s returned %s %s', str(self), str(nodes), str(names))
# store the dependencies in the cache
bld.node_deps[key] = nodes
bld.raw_deps[key] = names
# recompute the signature and return it
try:
sig = self.compute_sig_implicit_deps()
except KeyError:
try:
nodes = []
for k in bld.node_deps.get(self.unique_id(), []):
if k.id & 3 == 2: # Node.FILE:
if not k.id in bld.node_sigs[0]:
nodes.append(k)
else:
if not k.id in bld.node_sigs[self.env.variant()]:
nodes.append(k)
except:
nodes = '?'
raise Utils.WafError('Missing node signature for %r (for implicit dependencies %r)' % (nodes, self))
return sig
def compute_sig_implicit_deps(self):
"""it is intended for .cpp and inferred .h files
there is a single list (no tree traversal)
this is the hot spot so ... do not touch"""
upd = self.m.update
bld = self.generator.bld
tstamp = bld.node_sigs
env = self.env
for k in bld.node_deps.get(self.unique_id(), []):
# unlikely but necessary if it happens
if not k.parent.id in bld.cache_scanned_folders:
# if the parent folder is removed, an OSError may be thrown
bld.rescan(k.parent)
# if the parent folder is removed, a KeyError will be thrown
if k.id & 3 == 2: # Node.FILE:
upd(tstamp[0][k.id])
else:
upd(tstamp[env.variant()][k.id])
return self.m.digest()
def funex(c):
dc = {}
exec(c, dc)
return dc['f']
reg_act = re.compile(r"(?P<backslash>\\)|(?P<dollar>\$\$)|(?P<subst>\$\{(?P<var>\w+)(?P<code>.*?)\})", re.M)
def compile_fun_shell(name, line):
"""Compiles a string (once) into a function, eg:
simple_task_type('c++', '${CXX} -o ${TGT[0]} ${SRC} -I ${SRC[0].parent.bldpath()}')
The env variables (CXX, ..) on the task must not hold dicts (order)
The reserved keywords TGT and SRC represent the task input and output nodes
quick test:
bld(source='wscript', rule='echo "foo\\${SRC[0].name}\\bar"')
"""
extr = []
def repl(match):
g = match.group
if g('dollar'): return "$"
elif g('backslash'): return '\\\\'
elif g('subst'): extr.append((g('var'), g('code'))); return "%s"
return None
line = reg_act.sub(repl, line)
parm = []
dvars = []
app = parm.append
for (var, meth) in extr:
if var == 'SRC':
if meth: app('task.inputs%s' % meth)
else: app('" ".join([a.srcpath(env) for a in task.inputs])')
elif var == 'TGT':
if meth: app('task.outputs%s' % meth)
else: app('" ".join([a.bldpath(env) for a in task.outputs])')
else:
if not var in dvars: dvars.append(var)
app("p('%s')" % var)
if parm: parm = "%% (%s) " % (',\n\t\t'.join(parm))
else: parm = ''
c = COMPILE_TEMPLATE_SHELL % (line, parm)
debug('action: %s', c)
return (funex(c), dvars)
def compile_fun_noshell(name, line):
extr = []
def repl(match):
g = match.group
if g('dollar'): return "$"
elif g('subst'): extr.append((g('var'), g('code'))); return "<<|@|>>"
return None
line2 = reg_act.sub(repl, line)
params = line2.split('<<|@|>>')
buf = []
dvars = []
app = buf.append
for x in xrange(len(extr)):
params[x] = params[x].strip()
if params[x]:
app("lst.extend(%r)" % params[x].split())
(var, meth) = extr[x]
if var == 'SRC':
if meth: app('lst.append(task.inputs%s)' % meth)
else: app("lst.extend([a.srcpath(env) for a in task.inputs])")
elif var == 'TGT':
if meth: app('lst.append(task.outputs%s)' % meth)
else: app("lst.extend([a.bldpath(env) for a in task.outputs])")
else:
app('lst.extend(to_list(env[%r]))' % var)
if not var in dvars: dvars.append(var)
if params[-1]:
app("lst.extend(%r)" % shlex.split(params[-1]))
fun = COMPILE_TEMPLATE_NOSHELL % "\n\t".join(buf)
debug('action: %s', fun)
return (funex(fun), dvars)
def compile_fun(name, line, shell=None):
"commands can be launched by the shell or not"
if line.find('<') > 0 or line.find('>') > 0 or line.find('&&') > 0:
shell = True
#else:
# shell = False
if shell is None:
if sys.platform == 'win32':
shell = False
else:
shell = True
if shell:
return compile_fun_shell(name, line)
else:
return compile_fun_noshell(name, line)
def simple_task_type(name, line, color='GREEN', vars=[], ext_in=[], ext_out=[], before=[], after=[], shell=None):
"""return a new Task subclass with the function run compiled from the line given"""
(fun, dvars) = compile_fun(name, line, shell)
fun.code = line
return task_type_from_func(name, fun, vars or dvars, color, ext_in, ext_out, before, after)
def task_type_from_func(name, func, vars=[], color='GREEN', ext_in=[], ext_out=[], before=[], after=[]):
"""return a new Task subclass with the function run compiled from the line given"""
params = {
'run': func,
'vars': vars,
'color': color,
'name': name,
'ext_in': Utils.to_list(ext_in),
'ext_out': Utils.to_list(ext_out),
'before': Utils.to_list(before),
'after': Utils.to_list(after),
}
cls = type(Task)(name, (Task,), params)
TaskBase.classes[name] = cls
return cls
def always_run(cls):
"""Set all task instances of this class to be executed whenever a build is started
The task signature is calculated, but the result of the comparation between
task signatures is bypassed
"""
old = cls.runnable_status
def always(self):
old(self)
return RUN_ME
cls.runnable_status = always
def update_outputs(cls):
"""When a command is always run, it is possible that the output only change
sometimes. By default the build node have as a hash the signature of the task
which may not change. With this, the output nodes (produced) are hashed,
and the hashes are set to the build nodes
This may avoid unnecessary recompilations, but it uses more resources
(hashing the output files) so it is not used by default
"""
old_post_run = cls.post_run
def post_run(self):
old_post_run(self)
bld = self.outputs[0].__class__.bld
for output in self.outputs:
bld.node_sigs[self.env.variant()][output.id] = Utils.h_file(output.abspath(self.env))
cls.post_run = post_run
old_runnable_status = cls.runnable_status
def runnable_status(self):
status = old_runnable_status(self)
if status != RUN_ME:
return status
try:
bld = self.outputs[0].__class__.bld
new_sig = self.signature()
prev_sig = bld.task_sigs[self.unique_id()][0]
if prev_sig == new_sig:
for x in self.outputs:
if not x.id in bld.node_sigs[self.env.variant()]:
return RUN_ME
return SKIP_ME
except KeyError:
pass
except IndexError:
pass
return RUN_ME
cls.runnable_status = runnable_status
def extract_outputs(tasks):
"""file_deps: Infer additional dependencies from task input and output nodes
"""
v = {}
for x in tasks:
try:
(ins, outs) = v[x.env.variant()]
except KeyError:
ins = {}
outs = {}
v[x.env.variant()] = (ins, outs)
for a in getattr(x, 'inputs', []):
try: ins[a.id].append(x)
except KeyError: ins[a.id] = [x]
for a in getattr(x, 'outputs', []):
try: outs[a.id].append(x)
except KeyError: outs[a.id] = [x]
for (ins, outs) in v.values():
links = set(ins.iterkeys()).intersection(outs.iterkeys())
for k in links:
for a in ins[k]:
for b in outs[k]:
a.set_run_after(b)
def extract_deps(tasks):
"""file_deps: Infer additional dependencies from task input and output nodes and from implicit dependencies
returned by the scanners - that will only work if all tasks are created
this is aimed at people who have pathological builds and who do not care enough
to implement the build dependencies properly
with two loops over the list of tasks, do not expect this to be really fast
"""
# first reuse the function above
extract_outputs(tasks)
# map the output nodes to the tasks producing them
out_to_task = {}
for x in tasks:
v = x.env.variant()
try:
lst = x.outputs
except AttributeError:
pass
else:
for node in lst:
out_to_task[(v, node.id)] = x
# map the dependencies found to the tasks compiled
dep_to_task = {}
for x in tasks:
try:
x.signature()
except: # this is on purpose
pass
v = x.env.variant()
key = x.unique_id()
for k in x.generator.bld.node_deps.get(x.unique_id(), []):
try: dep_to_task[(v, k.id)].append(x)
except KeyError: dep_to_task[(v, k.id)] = [x]
# now get the intersection
deps = set(dep_to_task.keys()).intersection(set(out_to_task.keys()))
# and add the dependencies from task to task
for idx in deps:
for k in dep_to_task[idx]:
k.set_run_after(out_to_task[idx])
# cleanup, remove the signatures
for x in tasks:
try:
delattr(x, 'cache_sig')
except AttributeError:
pass
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2005-2008 (ita)
"""
The class task_gen encapsulates the creation of task objects (low-level code)
The instances can have various parameters, but the creation of task nodes (Task.py)
is delayed. To achieve this, various methods are called from the method "apply"
The class task_gen contains lots of methods, and a configuration table:
* the methods to call (self.meths) can be specified dynamically (removing, adding, ..)
* the order of the methods (self.prec or by default task_gen.prec) is configurable
* new methods can be inserted dynamically without pasting old code
Additionally, task_gen provides the method apply_core
* file extensions are mapped to methods: def meth(self, name_or_node)
* if a mapping is not found in self.mappings, it is searched in task_gen.mappings
* when called, the functions may modify self.allnodes to re-add source to process
* the mappings can map an extension or a filename (see the code below)
WARNING: subclasses must reimplement the clone method
"""
import os, traceback, copy
import Build, Task, Utils, Logs, Options
from Logs import debug, error, warn
from Constants import *
typos = {
'sources':'source',
'targets':'target',
'include':'includes',
'define':'defines',
'importpath':'importpaths',
'install_var':'install_path',
'install_subdir':'install_path',
'inst_var':'install_path',
'inst_dir':'install_path',
'feature':'features',
}
class register_obj(type):
"""no decorators for classes, so we use a metaclass
we store into task_gen.classes the classes that inherit task_gen
and whose names end in '_taskgen'
"""
def __init__(cls, name, bases, dict):
super(register_obj, cls).__init__(name, bases, dict)
name = cls.__name__
suffix = '_taskgen'
if name.endswith(suffix):
task_gen.classes[name.replace(suffix, '')] = cls
class task_gen(object):
"""
Most methods are of the form 'def meth(self):' without any parameters
there are many of them, and they do many different things:
* task creation
* task results installation
* environment modification
* attribute addition/removal
The inheritance approach is complicated
* mixing several languages at once
* subclassing is needed even for small changes
* inserting new methods is complicated
This new class uses a configuration table:
* adding new methods easily
* obtaining the order in which to call the methods
* postponing the method calls (post() -> apply)
Additionally, a 'traits' static attribute is provided:
* this list contains methods
* the methods can remove or add methods from self.meths
Example1: the attribute 'staticlib' is set on an instance
a method set in the list of traits is executed when the
instance is posted, it finds that flag and adds another method for execution
Example2: a method set in the list of traits finds the msvc
compiler (from self.env['MSVC']==1); more methods are added to self.meths
"""
__metaclass__ = register_obj
mappings = {}
mapped = {}
prec = Utils.DefaultDict(list)
traits = Utils.DefaultDict(set)
classes = {}
def __init__(self, *kw, **kwargs):
self.prec = Utils.DefaultDict(list)
"map precedence of function names to call"
# so we will have to play with directed acyclic graphs
# detect cycles, etc
self.source = ''
self.target = ''
# list of methods to execute - does not touch it by hand unless you know
self.meths = []
# list of mappings extension -> function
self.mappings = {}
# list of features (see the documentation on traits)
self.features = list(kw)
# not always a good idea
self.tasks = []
self.default_chmod = O644
self.default_install_path = None
# kind of private, beware of what you put in it, also, the contents are consumed
self.allnodes = []
self.bld = kwargs.get('bld', Build.bld)
self.env = self.bld.env.copy()
self.path = self.bld.path # emulate chdir when reading scripts
self.name = '' # give a name to the target (static+shlib with the same targetname ambiguity)
# provide a unique id
self.idx = self.bld.idx[self.path.id] = self.bld.idx.get(self.path.id, 0) + 1
for key, val in kwargs.iteritems():
setattr(self, key, val)
self.bld.task_manager.add_task_gen(self)
self.bld.all_task_gen.append(self)
def __str__(self):
return ("<task_gen '%s' of type %s defined in %s>"
% (self.name or self.target, self.__class__.__name__, str(self.path)))
def __setattr__(self, name, attr):
real = typos.get(name, name)
if real != name:
warn('typo %s -> %s' % (name, real))
if Logs.verbose > 0:
traceback.print_stack()
object.__setattr__(self, real, attr)
def to_list(self, value):
"helper: returns a list"
if isinstance(value, str): return value.split()
else: return value
def apply(self):
"order the methods to execute using self.prec or task_gen.prec"
keys = set(self.meths)
# add the methods listed in the features
self.features = Utils.to_list(self.features)
for x in self.features + ['*']:
st = task_gen.traits[x]
if not st:
warn('feature %r does not exist - bind at least one method to it' % x)
keys.update(st)
# copy the precedence table
prec = {}
prec_tbl = self.prec or task_gen.prec
for x in prec_tbl:
if x in keys:
prec[x] = prec_tbl[x]
# elements disconnected
tmp = []
for a in keys:
for x in prec.values():
if a in x: break
else:
tmp.append(a)
# topological sort
out = []
while tmp:
e = tmp.pop()
if e in keys: out.append(e)
try:
nlst = prec[e]
except KeyError:
pass
else:
del prec[e]
for x in nlst:
for y in prec:
if x in prec[y]:
break
else:
tmp.append(x)
if prec: raise Utils.WafError("graph has a cycle %s" % str(prec))
out.reverse()
self.meths = out
# then we run the methods in order
debug('task_gen: posting %s %d', self, id(self))
for x in out:
try:
v = getattr(self, x)
except AttributeError:
raise Utils.WafError("tried to retrieve %s which is not a valid method" % x)
debug('task_gen: -> %s (%d)', x, id(self))
v()
def post(self):
"runs the code to create the tasks, do not subclass"
if not self.name:
if isinstance(self.target, list):
self.name = ' '.join(self.target)
else:
self.name = self.target
if getattr(self, 'posted', None):
#error("OBJECT ALREADY POSTED" + str( self))
return
self.apply()
debug('task_gen: posted %s', self.name)
self.posted = True
def get_hook(self, ext):
try: return self.mappings[ext]
except KeyError:
try: return task_gen.mappings[ext]
except KeyError: return None
# TODO waf 1.6: always set the environment
# TODO waf 1.6: create_task(self, name, inputs, outputs)
def create_task(self, name, src=None, tgt=None, env=None):
env = env or self.env
task = Task.TaskBase.classes[name](env.copy(), generator=self)
if src:
task.set_inputs(src)
if tgt:
task.set_outputs(tgt)
self.tasks.append(task)
return task
def name_to_obj(self, name):
return self.bld.name_to_obj(name, self.env)
def find_sources_in_dirs(self, dirnames, excludes=[], exts=[]):
"""
The attributes "excludes" and "exts" must be lists to avoid the confusion
find_sources_in_dirs('a', 'b', 'c') <-> find_sources_in_dirs('a b c')
do not use absolute paths
do not use paths outside of the source tree
the files or folder beginning by . are not returned
# TODO: remove in Waf 1.6
"""
err_msg = "'%s' attribute must be a list"
if not isinstance(excludes, list):
raise Utils.WscriptError(err_msg % 'excludes')
if not isinstance(exts, list):
raise Utils.WscriptError(err_msg % 'exts')
lst = []
#make sure dirnames is a list helps with dirnames with spaces
dirnames = self.to_list(dirnames)
ext_lst = exts or list(self.mappings.keys()) + list(task_gen.mappings.keys())
for name in dirnames:
anode = self.path.find_dir(name)
if not anode or not anode.is_child_of(self.bld.srcnode):
raise Utils.WscriptError("Unable to use '%s' - either because it's not a relative path" \
", or it's not child of '%s'." % (name, self.bld.srcnode))
self.bld.rescan(anode)
for name in self.bld.cache_dir_contents[anode.id]:
# ignore hidden files
if name.startswith('.'):
continue
(base, ext) = os.path.splitext(name)
if ext in ext_lst and not name in lst and not name in excludes:
lst.append((anode.relpath_gen(self.path) or '.') + os.path.sep + name)
lst.sort()
self.source = self.to_list(self.source)
if not self.source: self.source = lst
else: self.source += lst
def clone(self, env):
""
newobj = task_gen(bld=self.bld)
for x in self.__dict__:
if x in ['env', 'bld']:
continue
elif x in ["path", "features"]:
setattr(newobj, x, getattr(self, x))
else:
setattr(newobj, x, copy.copy(getattr(self, x)))
newobj.__class__ = self.__class__
if isinstance(env, str):
newobj.env = self.bld.all_envs[env].copy()
else:
newobj.env = env.copy()
return newobj
def get_inst_path(self):
return getattr(self, '_install_path', getattr(self, 'default_install_path', ''))
def set_inst_path(self, val):
self._install_path = val
install_path = property(get_inst_path, set_inst_path)
def get_chmod(self):
return getattr(self, '_chmod', getattr(self, 'default_chmod', O644))
def set_chmod(self, val):
self._chmod = val
chmod = property(get_chmod, set_chmod)
def declare_extension(var, func):
try:
for x in Utils.to_list(var):
task_gen.mappings[x] = func
except:
raise Utils.WscriptError('declare_extension takes either a list or a string %r' % var)
task_gen.mapped[func.__name__] = func
def declare_order(*k):
assert(len(k) > 1)
n = len(k) - 1
for i in xrange(n):
f1 = k[i]
f2 = k[i+1]
if not f1 in task_gen.prec[f2]:
task_gen.prec[f2].append(f1)
def declare_chain(name='', action='', ext_in='', ext_out='', reentrant=True, color='BLUE',
install=0, before=[], after=[], decider=None, rule=None, scan=None):
"""
see Tools/flex.py for an example
while i do not like such wrappers, some people really do
"""
action = action or rule
if isinstance(action, str):
act = Task.simple_task_type(name, action, color=color)
else:
act = Task.task_type_from_func(name, action, color=color)
act.ext_in = tuple(Utils.to_list(ext_in))
act.ext_out = tuple(Utils.to_list(ext_out))
act.before = Utils.to_list(before)
act.after = Utils.to_list(after)
act.scan = scan
def x_file(self, node):
if decider:
ext = decider(self, node)
else:
ext = ext_out
if isinstance(ext, str):
out_source = node.change_ext(ext)
if reentrant:
self.allnodes.append(out_source)
elif isinstance(ext, list):
out_source = [node.change_ext(x) for x in ext]
if reentrant:
for i in xrange((reentrant is True) and len(out_source) or reentrant):
self.allnodes.append(out_source[i])
else:
# XXX: useless: it will fail on Utils.to_list above...
raise Utils.WafError("do not know how to process %s" % str(ext))
tsk = self.create_task(name, node, out_source)
if node.__class__.bld.is_install:
tsk.install = install
declare_extension(act.ext_in, x_file)
def bind_feature(name, methods):
lst = Utils.to_list(methods)
task_gen.traits[name].update(lst)
"""
All the following decorators are registration decorators, i.e add an attribute to current class
(task_gen and its derivatives), with same name as func, which points to func itself.
For example:
@taskgen
def sayHi(self):
print("hi")
Now taskgen.sayHi() may be called
If python were really smart, it could infer itself the order of methods by looking at the
attributes. A prerequisite for execution is to have the attribute set before.
Intelligent compilers binding aspect-oriented programming and parallelization, what a nice topic for studies.
"""
def taskgen(func):
setattr(task_gen, func.__name__, func)
return func
def feature(*k):
def deco(func):
setattr(task_gen, func.__name__, func)
for name in k:
task_gen.traits[name].update([func.__name__])
return func
return deco
def before(*k):
def deco(func):
setattr(task_gen, func.__name__, func)
for fun_name in k:
if not func.__name__ in task_gen.prec[fun_name]:
task_gen.prec[fun_name].append(func.__name__)
return func
return deco
def after(*k):
def deco(func):
setattr(task_gen, func.__name__, func)
for fun_name in k:
if not fun_name in task_gen.prec[func.__name__]:
task_gen.prec[func.__name__].append(fun_name)
return func
return deco
def extension(var):
def deco(func):
setattr(task_gen, func.__name__, func)
try:
for x in Utils.to_list(var):
task_gen.mappings[x] = func
except:
raise Utils.WafError('extension takes either a list or a string %r' % var)
task_gen.mapped[func.__name__] = func
return func
return deco
# TODO make certain the decorators may be used here
def apply_core(self):
"""Process the attribute source
transform the names into file nodes
try to process the files by name first, later by extension"""
# get the list of folders to use by the scanners
# all our objects share the same include paths anyway
find_resource = self.path.find_resource
for filename in self.to_list(self.source):
# if self.mappings or task_gen.mappings contains a file of the same name
x = self.get_hook(filename)
if x:
x(self, filename)
else:
node = find_resource(filename)
if not node: raise Utils.WafError("source not found: '%s' in '%s'" % (filename, str(self.path)))
self.allnodes.append(node)
for node in self.allnodes:
# self.mappings or task_gen.mappings map the file extension to a function
x = self.get_hook(node.suffix())
if not x:
raise Utils.WafError("Cannot guess how to process %s (got mappings %r in %r) -> try conf.check_tool(..)?" % \
(str(node), self.__class__.mappings.keys(), self.__class__))
x(self, node)
feature('*')(apply_core)
def exec_rule(self):
"""Process the attribute rule, when provided the method apply_core will be disabled
"""
if not getattr(self, 'rule', None):
return
# someone may have removed it already
try:
self.meths.remove('apply_core')
except ValueError:
pass
# get the function and the variables
func = self.rule
vars2 = []
if isinstance(func, str):
# use the shell by default for user-defined commands
(func, vars2) = Task.compile_fun('', self.rule, shell=getattr(self, 'shell', True))
func.code = self.rule
# create the task class
name = getattr(self, 'name', None) or self.target or self.rule
if not isinstance(name, str):
name = str(self.idx)
cls = Task.task_type_from_func(name, func, getattr(self, 'vars', vars2))
# now create one instance
tsk = self.create_task(name)
dep_vars = getattr(self, 'dep_vars', ['ruledeps'])
if dep_vars:
tsk.dep_vars = dep_vars
if isinstance(self.rule, str):
tsk.env.ruledeps = self.rule
else:
# only works if the function is in a global module such as a waf tool
tsk.env.ruledeps = Utils.h_fun(self.rule)
# we assume that the user knows that without inputs or outputs
#if not getattr(self, 'target', None) and not getattr(self, 'source', None):
# cls.quiet = True
if getattr(self, 'target', None):
cls.quiet = True
tsk.outputs = [self.path.find_or_declare(x) for x in self.to_list(self.target)]
if getattr(self, 'source', None):
cls.quiet = True
tsk.inputs = []
for x in self.to_list(self.source):
y = self.path.find_resource(x)
if not y:
raise Utils.WafError('input file %r could not be found (%r)' % (x, self.path.abspath()))
tsk.inputs.append(y)
if self.allnodes:
tsk.inputs.extend(self.allnodes)
if getattr(self, 'scan', None):
cls.scan = self.scan
if getattr(self, 'install_path', None):
tsk.install_path = self.install_path
if getattr(self, 'cwd', None):
tsk.cwd = self.cwd
if getattr(self, 'on_results', None):
Task.update_outputs(cls)
if getattr(self, 'always', None):
Task.always_run(cls)
for x in ['after', 'before', 'ext_in', 'ext_out']:
setattr(cls, x, getattr(self, x, []))
feature('*')(exec_rule)
before('apply_core')(exec_rule)
def sequence_order(self):
"""
add a strict sequential constraint between the tasks generated by task generators
it uses the fact that task generators are posted in order
it will not post objects which belong to other folders
there is also an awesome trick for executing the method in last position
to use:
bld(features='javac seq')
bld(features='jar seq')
to start a new sequence, set the attribute seq_start, for example:
obj.seq_start = True
"""
if self.meths and self.meths[-1] != 'sequence_order':
self.meths.append('sequence_order')
return
if getattr(self, 'seq_start', None):
return
# all the tasks previously declared must be run before these
if getattr(self.bld, 'prev', None):
self.bld.prev.post()
for x in self.bld.prev.tasks:
for y in self.tasks:
y.set_run_after(x)
self.bld.prev = self
feature('seq')(sequence_order)
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2006 (ita)
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2006-2008 (ita)
# Ralf Habacker, 2006 (rh)
"ar and ranlib"
import os, sys
import Task, Utils
from Configure import conftest
ar_str = '${AR} ${ARFLAGS} ${AR_TGT_F}${TGT} ${AR_SRC_F}${SRC}'
cls = Task.simple_task_type('static_link', ar_str, color='YELLOW', ext_in='.o', ext_out='.bin', shell=False)
cls.maxjobs = 1
cls.install = Utils.nada
# remove the output in case it already exists
old = cls.run
def wrap(self):
try: os.remove(self.outputs[0].abspath(self.env))
except OSError: pass
return old(self)
setattr(cls, 'run', wrap)
def detect(conf):
conf.find_program('ar', var='AR')
conf.find_program('ranlib', var='RANLIB')
conf.env.ARFLAGS = 'rcs'
@conftest
def find_ar(conf):
v = conf.env
conf.check_tool('ar')
if not v['AR']: conf.fatal('ar is required for static libraries - not found')
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2006 (ita)
"Base for c programs/libraries"
import os
import TaskGen, Build, Utils, Task
from Logs import debug
import ccroot
from TaskGen import feature, before, extension, after
g_cc_flag_vars = [
'CCDEPS', 'FRAMEWORK', 'FRAMEWORKPATH',
'STATICLIB', 'LIB', 'LIBPATH', 'LINKFLAGS', 'RPATH',
'CCFLAGS', 'CPPPATH', 'CPPFLAGS', 'CCDEFINES']
EXT_CC = ['.c']
g_cc_type_vars = ['CCFLAGS', 'LINKFLAGS']
# TODO remove in waf 1.6
class cc_taskgen(ccroot.ccroot_abstract):
pass
@feature('cc')
@before('apply_type_vars')
@after('default_cc')
def init_cc(self):
self.p_flag_vars = set(self.p_flag_vars).union(g_cc_flag_vars)
self.p_type_vars = set(self.p_type_vars).union(g_cc_type_vars)
if not self.env['CC_NAME']:
raise Utils.WafError("At least one compiler (gcc, ..) must be selected")
@feature('cc')
@after('apply_incpaths')
def apply_obj_vars_cc(self):
"""after apply_incpaths for INC_PATHS"""
env = self.env
app = env.append_unique
cpppath_st = env['CPPPATH_ST']
# local flags come first
# set the user-defined includes paths
for i in env['INC_PATHS']:
app('_CCINCFLAGS', cpppath_st % i.bldpath(env))
app('_CCINCFLAGS', cpppath_st % i.srcpath(env))
# set the library include paths
for i in env['CPPPATH']:
app('_CCINCFLAGS', cpppath_st % i)
@feature('cc')
@after('apply_lib_vars')
def apply_defines_cc(self):
"""after uselib is set for CCDEFINES"""
self.defines = getattr(self, 'defines', [])
lst = self.to_list(self.defines) + self.to_list(self.env['CCDEFINES'])
milst = []
# now process the local defines
for defi in lst:
if not defi in milst:
milst.append(defi)
# CCDEFINES_
libs = self.to_list(self.uselib)
for l in libs:
val = self.env['CCDEFINES_'+l]
if val: milst += val
self.env['DEFLINES'] = ["%s %s" % (x[0], Utils.trimquotes('='.join(x[1:]))) for x in [y.split('=') for y in milst]]
y = self.env['CCDEFINES_ST']
self.env['_CCDEFFLAGS'] = [y%x for x in milst]
@extension(EXT_CC)
def c_hook(self, node):
# create the compilation task: cpp or cc
if getattr(self, 'obj_ext', None):
obj_ext = self.obj_ext
else:
obj_ext = '_%d.o' % self.idx
task = self.create_task('cc', node, node.change_ext(obj_ext))
try:
self.compiled_tasks.append(task)
except AttributeError:
raise Utils.WafError('Have you forgotten to set the feature "cc" on %s?' % str(self))
return task
cc_str = '${CC} ${CCFLAGS} ${CPPFLAGS} ${_CCINCFLAGS} ${_CCDEFFLAGS} ${CC_SRC_F}${SRC} ${CC_TGT_F}${TGT}'
cls = Task.simple_task_type('cc', cc_str, 'GREEN', ext_out='.o', ext_in='.c', shell=False)
cls.scan = ccroot.scan
cls.vars.append('CCDEPS')
link_str = '${LINK_CC} ${CCLNK_SRC_F}${SRC} ${CCLNK_TGT_F}${TGT[0].abspath(env)} ${LINKFLAGS}'
cls = Task.simple_task_type('cc_link', link_str, color='YELLOW', ext_in='.o', ext_out='.bin', shell=False)
cls.maxjobs = 1
cls.install = Utils.nada
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2005-2008 (ita)
"base for all c/c++ programs and libraries"
import os, sys, re
import TaskGen, Task, Utils, preproc, Logs, Build, Options
from Logs import error, debug, warn
from Utils import md5
from TaskGen import taskgen, after, before, feature
from Constants import *
from Configure import conftest
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
import config_c # <- necessary for the configuration, do not touch
USE_TOP_LEVEL = False
def get_cc_version(conf, cc, gcc=False, icc=False):
cmd = cc + ['-dM', '-E', '-']
try:
p = Utils.pproc.Popen(cmd, stdin=Utils.pproc.PIPE, stdout=Utils.pproc.PIPE, stderr=Utils.pproc.PIPE)
p.stdin.write('\n')
out = p.communicate()[0]
except:
conf.fatal('could not determine the compiler version %r' % cmd)
# PY3K: do not touch
out = str(out)
if gcc:
if out.find('__INTEL_COMPILER') >= 0:
conf.fatal('The intel compiler pretends to be gcc')
if out.find('__GNUC__') < 0:
conf.fatal('Could not determine the compiler type')
if icc and out.find('__INTEL_COMPILER') < 0:
conf.fatal('Not icc/icpc')
k = {}
if icc or gcc:
out = out.split('\n')
import shlex
for line in out:
lst = shlex.split(line)
if len(lst)>2:
key = lst[1]
val = lst[2]
k[key] = val
def isD(var):
return var in k
def isT(var):
return var in k and k[var] != '0'
# Some documentation is available at http://predef.sourceforge.net
# The names given to DEST_OS must match what Utils.unversioned_sys_platform() returns.
mp1 = {
'__linux__' : 'linux',
'__GNU__' : 'hurd',
'__FreeBSD__' : 'freebsd',
'__NetBSD__' : 'netbsd',
'__OpenBSD__' : 'openbsd',
'__sun' : 'sunos',
'__hpux' : 'hpux',
'__sgi' : 'irix',
'_AIX' : 'aix',
'__CYGWIN__' : 'cygwin',
'__MSYS__' : 'msys',
'_UWIN' : 'uwin',
'_WIN64' : 'win32',
'_WIN32' : 'win32',
}
for i in mp1:
if isD(i):
conf.env.DEST_OS = mp1[i]
break
else:
if isD('__APPLE__') and isD('__MACH__'):
conf.env.DEST_OS = 'darwin'
elif isD('__unix__'): # unix must be tested last as it's a generic fallback
conf.env.DEST_OS = 'generic'
if isD('__ELF__'):
conf.env.DEST_BINFMT = 'elf'
mp2 = {
'__x86_64__' : 'x86_64',
'__i386__' : 'x86',
'__ia64__' : 'ia',
'__mips__' : 'mips',
'__sparc__' : 'sparc',
'__alpha__' : 'alpha',
'__arm__' : 'arm',
'__hppa__' : 'hppa',
'__powerpc__' : 'powerpc',
}
for i in mp2:
if isD(i):
conf.env.DEST_CPU = mp2[i]
break
debug('ccroot: dest platform: ' + ' '.join([conf.env[x] or '?' for x in ('DEST_OS', 'DEST_BINFMT', 'DEST_CPU')]))
conf.env['CC_VERSION'] = (k['__GNUC__'], k['__GNUC_MINOR__'], k['__GNUC_PATCHLEVEL__'])
return k
class DEBUG_LEVELS:
"""Will disappear in waf 1.6"""
ULTRADEBUG = "ultradebug"
DEBUG = "debug"
RELEASE = "release"
OPTIMIZED = "optimized"
CUSTOM = "custom"
ALL = [ULTRADEBUG, DEBUG, RELEASE, OPTIMIZED, CUSTOM]
def scan(self):
"look for .h the .cpp need"
debug('ccroot: _scan_preprocessor(self, node, env, path_lst)')
# TODO waf 1.6 - assume the default input has exactly one file
if len(self.inputs) == 1:
node = self.inputs[0]
(nodes, names) = preproc.get_deps(node, self.env, nodepaths = self.env['INC_PATHS'])
if Logs.verbose:
debug('deps: deps for %s: %r; unresolved %r', str(node), nodes, names)
return (nodes, names)
all_nodes = []
all_names = []
seen = set()
for node in self.inputs:
(nodes, names) = preproc.get_deps(node, self.env, nodepaths = self.env['INC_PATHS'])
if Logs.verbose:
debug('deps: deps for %s: %r; unresolved %r', str(node), nodes, names)
for x in nodes:
if id(x) in seen: continue
seen.add(id(x))
all_nodes.append(x)
for x in names:
if not x in all_names:
all_names.append(x)
return (all_nodes, all_names)
class ccroot_abstract(TaskGen.task_gen):
"Parent class for programs and libraries in languages c, c++ and moc (Qt)"
def __init__(self, *k, **kw):
# COMPAT remove in waf 1.6 TODO
if len(k) > 1:
k = list(k)
if k[1][0] != 'c':
k[1] = 'c' + k[1]
TaskGen.task_gen.__init__(self, *k, **kw)
def get_target_name(self):
tp = 'program'
for x in self.features:
if x in ['cshlib', 'cstaticlib']:
tp = x.lstrip('c')
pattern = self.env[tp + '_PATTERN']
if not pattern: pattern = '%s'
dir, name = os.path.split(self.target)
if self.env.DEST_BINFMT == 'pe' and getattr(self, 'vnum', None) and 'cshlib' in self.features:
# include the version in the dll file name,
# the import lib file name stays unversionned.
name = name + '-' + self.vnum.split('.')[0]
return os.path.join(dir, pattern % name)
@feature('cc', 'cxx')
@before('apply_core')
def default_cc(self):
"""compiled_tasks attribute must be set before the '.c->.o' tasks can be created"""
Utils.def_attrs(self,
includes = '',
defines= '',
rpaths = '',
uselib = '',
uselib_local = '',
add_objects = '',
p_flag_vars = [],
p_type_vars = [],
compiled_tasks = [],
link_task = None)
# The only thing we need for cross-compilation is DEST_BINFMT.
# At some point, we may reach a case where DEST_BINFMT is not enough, but for now it's sufficient.
# Currently, cross-compilation is auto-detected only for the gnu and intel compilers.
if not self.env.DEST_BINFMT:
# Infer the binary format from the os name.
self.env.DEST_BINFMT = Utils.unversioned_sys_platform_to_binary_format(
self.env.DEST_OS or Utils.unversioned_sys_platform())
if not self.env.BINDIR: self.env.BINDIR = Utils.subst_vars('${PREFIX}/bin', self.env)
if not self.env.LIBDIR: self.env.LIBDIR = Utils.subst_vars('${PREFIX}/lib${LIB_EXT}', self.env)
@feature('cprogram', 'dprogram', 'cstaticlib', 'dstaticlib', 'cshlib', 'dshlib')
def apply_verif(self):
"""no particular order, used for diagnostic"""
if not (self.source or getattr(self, 'add_objects', None) or getattr(self, 'uselib_local', None) or getattr(self, 'obj_files', None)):
raise Utils.WafError('no source files specified for %s' % self)
if not self.target:
raise Utils.WafError('no target for %s' % self)
# TODO reference the d programs, shlibs in d.py, not here
@feature('cprogram', 'dprogram')
@after('default_cc')
@before('apply_core')
def vars_target_cprogram(self):
self.default_install_path = self.env.BINDIR
self.default_chmod = O755
@after('default_cc')
@feature('cshlib', 'dshlib')
@before('apply_core')
def vars_target_cshlib(self):
if self.env.DEST_BINFMT == 'pe':
# set execute bit on libs to avoid 'permission denied' (issue 283)
self.default_chmod = O755
self.default_install_path = self.env.BINDIR
else:
self.default_install_path = self.env.LIBDIR
@feature('cprogram', 'dprogram', 'cstaticlib', 'dstaticlib', 'cshlib', 'dshlib')
@after('apply_link', 'vars_target_cprogram', 'vars_target_cshlib')
def default_link_install(self):
"""you may kill this method to inject your own installation for the first element
any other install should only process its own nodes and not those from the others"""
if self.install_path:
self.bld.install_files(self.install_path, self.link_task.outputs[0], env=self.env, chmod=self.chmod)
@feature('cc', 'cxx')
@after('apply_type_vars', 'apply_lib_vars', 'apply_core')
def apply_incpaths(self):
"""used by the scanner
after processing the uselib for CPPPATH
after apply_core because some processing may add include paths
"""
lst = []
# TODO move the uselib processing out of here
for lib in self.to_list(self.uselib):
for path in self.env['CPPPATH_' + lib]:
if not path in lst:
lst.append(path)
if preproc.go_absolute:
for path in preproc.standard_includes:
if not path in lst:
lst.append(path)
for path in self.to_list(self.includes):
if not path in lst:
if preproc.go_absolute or not os.path.isabs(path):
lst.append(path)
else:
self.env.prepend_value('CPPPATH', path)
for path in lst:
node = None
if os.path.isabs(path):
if preproc.go_absolute:
node = self.bld.root.find_dir(path)
elif path[0] == '#':
node = self.bld.srcnode
if len(path) > 1:
node = node.find_dir(path[1:])
else:
node = self.path.find_dir(path)
if node:
self.env.append_value('INC_PATHS', node)
# TODO WAF 1.6
if USE_TOP_LEVEL:
self.env.append_value('INC_PATHS', self.bld.srcnode)
@feature('cc', 'cxx')
@after('init_cc', 'init_cxx')
@before('apply_lib_vars')
def apply_type_vars(self):
"""before apply_lib_vars because we modify uselib
after init_cc and init_cxx because web need p_type_vars
"""
for x in self.features:
if not x in ['cprogram', 'cstaticlib', 'cshlib']:
continue
x = x.lstrip('c')
# if the type defines uselib to add, add them
st = self.env[x + '_USELIB']
if st: self.uselib = self.uselib + ' ' + st
# each compiler defines variables like 'shlib_CXXFLAGS', 'shlib_LINKFLAGS', etc
# so when we make a task generator of the type shlib, CXXFLAGS are modified accordingly
for var in self.p_type_vars:
compvar = '%s_%s' % (x, var)
#print compvar
value = self.env[compvar]
if value: self.env.append_value(var, value)
@feature('cprogram', 'cshlib', 'cstaticlib')
@after('apply_core')
def apply_link(self):
"""executes after apply_core for collecting 'compiled_tasks'
use a custom linker if specified (self.link='name-of-custom-link-task')"""
link = getattr(self, 'link', None)
if not link:
if 'cstaticlib' in self.features: link = 'static_link'
elif 'cxx' in self.features: link = 'cxx_link'
else: link = 'cc_link'
tsk = self.create_task(link)
outputs = [t.outputs[0] for t in self.compiled_tasks]
tsk.set_inputs(outputs)
tsk.set_outputs(self.path.find_or_declare(get_target_name(self)))
self.link_task = tsk
@feature('cc', 'cxx')
@after('apply_link', 'init_cc', 'init_cxx', 'apply_core')
def apply_lib_vars(self):
"""after apply_link because of 'link_task'
after default_cc because of the attribute 'uselib'"""
# after 'apply_core' in case if 'cc' if there is no link
env = self.env
# 1. the case of the libs defined in the project (visit ancestors first)
# the ancestors external libraries (uselib) will be prepended
self.uselib = self.to_list(self.uselib)
names = self.to_list(self.uselib_local)
seen = set([])
tmp = Utils.deque(names) # consume a copy of the list of names
while tmp:
lib_name = tmp.popleft()
# visit dependencies only once
if lib_name in seen:
continue
y = self.name_to_obj(lib_name)
if not y:
raise Utils.WafError('object %r was not found in uselib_local (required by %r)' % (lib_name, self.name))
y.post()
seen.add(lib_name)
# object has ancestors to process (shared libraries): add them to the end of the list
if getattr(y, 'uselib_local', None):
lst = y.to_list(y.uselib_local)
if 'cshlib' in y.features or 'cprogram' in y.features:
lst = [x for x in lst if not 'cstaticlib' in self.name_to_obj(x).features]
tmp.extend(lst)
# link task and flags
if getattr(y, 'link_task', None):
link_name = y.target[y.target.rfind(os.sep) + 1:]
if 'cstaticlib' in y.features:
env.append_value('STATICLIB', link_name)
elif 'cshlib' in y.features or 'cprogram' in y.features:
# WARNING some linkers can link against programs
env.append_value('LIB', link_name)
# the order
self.link_task.set_run_after(y.link_task)
# for the recompilation
dep_nodes = getattr(self.link_task, 'dep_nodes', [])
self.link_task.dep_nodes = dep_nodes + y.link_task.outputs
# add the link path too
tmp_path = y.link_task.outputs[0].parent.bldpath(self.env)
if not tmp_path in env['LIBPATH']: env.prepend_value('LIBPATH', tmp_path)
# add ancestors uselib too - but only propagate those that have no staticlib
for v in self.to_list(y.uselib):
if not env['STATICLIB_' + v]:
if not v in self.uselib:
self.uselib.insert(0, v)
# if the library task generator provides 'export_incdirs', add to the include path
# the export_incdirs must be a list of paths relative to the other library
if getattr(y, 'export_incdirs', None):
for x in self.to_list(y.export_incdirs):
node = y.path.find_dir(x)
if not node:
raise Utils.WafError('object %r: invalid folder %r in export_incdirs' % (y.target, x))
self.env.append_unique('INC_PATHS', node)
# 2. the case of the libs defined outside
for x in self.uselib:
for v in self.p_flag_vars:
val = self.env[v + '_' + x]
if val: self.env.append_value(v, val)
@feature('cprogram', 'cstaticlib', 'cshlib')
@after('init_cc', 'init_cxx', 'apply_link')
def apply_objdeps(self):
"add the .o files produced by some other object files in the same manner as uselib_local"
if not getattr(self, 'add_objects', None): return
seen = []
names = self.to_list(self.add_objects)
while names:
x = names[0]
# visit dependencies only once
if x in seen:
names = names[1:]
continue
# object does not exist ?
y = self.name_to_obj(x)
if not y:
raise Utils.WafError('object %r was not found in uselib_local (required by add_objects %r)' % (x, self.name))
# object has ancestors to process first ? update the list of names
if getattr(y, 'add_objects', None):
added = 0
lst = y.to_list(y.add_objects)
lst.reverse()
for u in lst:
if u in seen: continue
added = 1
names = [u]+names
if added: continue # list of names modified, loop
# safe to process the current object
y.post()
seen.append(x)
for t in y.compiled_tasks:
self.link_task.inputs.extend(t.outputs)
@feature('cprogram', 'cshlib', 'cstaticlib')
@after('apply_lib_vars')
def apply_obj_vars(self):
"""after apply_lib_vars for uselib"""
v = self.env
lib_st = v['LIB_ST']
staticlib_st = v['STATICLIB_ST']
libpath_st = v['LIBPATH_ST']
staticlibpath_st = v['STATICLIBPATH_ST']
rpath_st = v['RPATH_ST']
app = v.append_unique
if v['FULLSTATIC']:
v.append_value('LINKFLAGS', v['FULLSTATIC_MARKER'])
for i in v['RPATH']:
if i and rpath_st:
app('LINKFLAGS', rpath_st % i)
for i in v['LIBPATH']:
app('LINKFLAGS', libpath_st % i)
app('LINKFLAGS', staticlibpath_st % i)
if v['STATICLIB']:
v.append_value('LINKFLAGS', v['STATICLIB_MARKER'])
k = [(staticlib_st % i) for i in v['STATICLIB']]
app('LINKFLAGS', k)
# fully static binaries ?
if not v['FULLSTATIC']:
if v['STATICLIB'] or v['LIB']:
v.append_value('LINKFLAGS', v['SHLIB_MARKER'])
app('LINKFLAGS', [lib_st % i for i in v['LIB']])
@after('apply_link')
def process_obj_files(self):
if not hasattr(self, 'obj_files'): return
for x in self.obj_files:
node = self.path.find_resource(x)
self.link_task.inputs.append(node)
@taskgen
def add_obj_file(self, file):
"""Small example on how to link object files as if they were source
obj = bld.create_obj('cc')
obj.add_obj_file('foo.o')"""
if not hasattr(self, 'obj_files'): self.obj_files = []
if not 'process_obj_files' in self.meths: self.meths.append('process_obj_files')
self.obj_files.append(file)
c_attrs = {
'cxxflag' : 'CXXFLAGS',
'cflag' : 'CCFLAGS',
'ccflag' : 'CCFLAGS',
'linkflag' : 'LINKFLAGS',
'ldflag' : 'LINKFLAGS',
'lib' : 'LIB',
'libpath' : 'LIBPATH',
'staticlib': 'STATICLIB',
'staticlibpath': 'STATICLIBPATH',
'rpath' : 'RPATH',
'framework' : 'FRAMEWORK',
'frameworkpath' : 'FRAMEWORKPATH'
}
@feature('cc', 'cxx')
@before('init_cxx', 'init_cc')
@before('apply_lib_vars', 'apply_obj_vars', 'apply_incpaths', 'init_cc')
def add_extra_flags(self):
"""case and plural insensitive
before apply_obj_vars for processing the library attributes
"""
for x in self.__dict__.keys():
y = x.lower()
if y[-1] == 's':
y = y[:-1]
if c_attrs.get(y, None):
self.env.append_unique(c_attrs[y], getattr(self, x))
# ============ the code above must not know anything about import libs ==========
@feature('cshlib')
@after('apply_link', 'default_cc')
@before('apply_lib_vars', 'apply_objdeps', 'default_link_install')
def apply_implib(self):
"""On mswindows, handle dlls and their import libs
the .dll.a is the import lib and it is required for linking so it is installed too
"""
if not self.env.DEST_BINFMT == 'pe':
return
self.meths.remove('default_link_install')
bindir = self.install_path
if not bindir: return
# install the dll in the bin dir
dll = self.link_task.outputs[0]
self.bld.install_files(bindir, dll, self.env, self.chmod)
# add linker flags to generate the import lib
implib = self.env['implib_PATTERN'] % os.path.split(self.target)[1]
implib = dll.parent.find_or_declare(implib)
self.link_task.outputs.append(implib)
self.bld.install_as('${LIBDIR}/%s' % implib.name, implib, self.env)
self.env.append_value('LINKFLAGS', (self.env['IMPLIB_ST'] % implib.bldpath(self.env)).split())
# ============ the code above must not know anything about vnum processing on unix platforms =========
@feature('cshlib')
@after('apply_link')
@before('apply_lib_vars', 'default_link_install')
def apply_vnum(self):
"""
libfoo.so is installed as libfoo.so.1.2.3
"""
if not getattr(self, 'vnum', '') or not 'cshlib' in self.features or os.name != 'posix' or self.env.DEST_BINFMT not in ('elf', 'mac-o'):
return
self.meths.remove('default_link_install')
link = self.link_task
nums = self.vnum.split('.')
node = link.outputs[0]
libname = node.name
if libname.endswith('.dylib'):
name3 = libname.replace('.dylib', '.%s.dylib' % self.vnum)
name2 = libname.replace('.dylib', '.%s.dylib' % nums[0])
else:
name3 = libname + '.' + self.vnum
name2 = libname + '.' + nums[0]
if self.env.SONAME_ST:
v = self.env.SONAME_ST % name2
self.env.append_value('LINKFLAGS', v.split())
bld = self.bld
nums = self.vnum.split('.')
path = self.install_path
if not path: return
bld.install_as(path + os.sep + name3, node, env=self.env)
bld.symlink_as(path + os.sep + name2, name3)
bld.symlink_as(path + os.sep + libname, name3)
# the following task is just to enable execution from the build dir :-/
tsk = self.create_task('vnum')
tsk.set_inputs([node])
tsk.set_outputs(node.parent.find_or_declare(name2))
def exec_vnum_link(self):
path = self.outputs[0].abspath(self.env)
try:
os.remove(path)
except OSError:
pass
try:
os.symlink(self.inputs[0].name, path)
except OSError:
return 1
cls = Task.task_type_from_func('vnum', func=exec_vnum_link, ext_in='.bin', color='CYAN')
cls.quiet = 1
# ============ the --as-needed flag should added during the configuration, not at runtime =========
@conftest
def add_as_needed(conf):
if conf.env.DEST_BINFMT == 'elf' and 'gcc' in (conf.env.CXX_NAME, conf.env.CC_NAME):
conf.env.append_unique('LINKFLAGS', '--as-needed')
#!/usr/bin/env python
# encoding: utf-8
# Matthias Jahn jahn dôt matthias ât freenet dôt de, 2007 (pmarat)
import os, sys, imp, types, ccroot
import optparse
import Utils, Configure, Options
from Logs import debug
c_compiler = {
'win32': ['gcc'],
'cygwin': ['gcc'],
'darwin': ['gcc'],
'aix': ['xlc', 'gcc'],
'linux': ['gcc', 'icc', 'suncc'],
'sunos': ['gcc', 'suncc'],
'irix': ['gcc'],
'hpux': ['gcc'],
'default': ['gcc']
}
def __list_possible_compiler(platform):
try:
return c_compiler[platform]
except KeyError:
return c_compiler["default"]
def detect(conf):
"""
for each compiler for the platform, try to configure the compiler
in theory the tools should raise a configuration error if the compiler
pretends to be something it is not (setting CC=icc and trying to configure gcc)
"""
try: test_for_compiler = Options.options.check_c_compiler
except AttributeError: conf.fatal("Add set_options(opt): opt.tool_options('compiler_cc')")
orig = conf.env
for compiler in test_for_compiler.split():
conf.env = orig.copy()
try:
conf.check_tool(compiler)
except Configure.ConfigurationError, e:
debug('compiler_cc: %r' % e)
else:
if conf.env['CC']:
orig.table = conf.env.get_merged_dict()
conf.env = orig
conf.check_message(compiler, '', True)
conf.env['COMPILER_CC'] = compiler
break
conf.check_message(compiler, '', False)
break
else:
conf.fatal('could not configure a c compiler!')
def set_options(opt):
build_platform = Utils.unversioned_sys_platform()
possible_compiler_list = __list_possible_compiler(build_platform)
test_for_compiler = ' '.join(possible_compiler_list)
cc_compiler_opts = opt.add_option_group("C Compiler Options")
cc_compiler_opts.add_option('--check-c-compiler', default="%s" % test_for_compiler,
help='On this platform (%s) the following C-Compiler will be checked by default: "%s"' % (build_platform, test_for_compiler),
dest="check_c_compiler")
for c_compiler in test_for_compiler.split():
opt.tool_options('%s' % c_compiler, option_group=cc_compiler_opts)
#!/usr/bin/env python
# encoding: utf-8
# Matthias Jahn jahn dôt matthias ât freenet dôt de 2007 (pmarat)
import os, sys, imp, types, ccroot
import optparse
import Utils, Configure, Options
from Logs import debug
cxx_compiler = {
'win32': ['g++'],
'cygwin': ['g++'],
'darwin': ['g++'],
'aix': ['xlc++', 'g++'],
'linux': ['g++', 'icpc', 'sunc++'],
'sunos': ['g++', 'sunc++'],
'irix': ['g++'],
'hpux': ['g++'],
'default': ['g++']
}
def __list_possible_compiler(platform):
try:
return cxx_compiler[platform]
except KeyError:
return cxx_compiler["default"]
def detect(conf):
try: test_for_compiler = Options.options.check_cxx_compiler
except AttributeError: raise Configure.ConfigurationError("Add set_options(opt): opt.tool_options('compiler_cxx')")
orig = conf.env
for compiler in test_for_compiler.split():
try:
conf.env = orig.copy()
conf.check_tool(compiler)
except Configure.ConfigurationError, e:
debug('compiler_cxx: %r' % e)
else:
if conf.env['CXX']:
orig.table = conf.env.get_merged_dict()
conf.env = orig
conf.check_message(compiler, '', True)
conf.env['COMPILER_CXX'] = compiler
break
conf.check_message(compiler, '', False)
break
else:
conf.fatal('could not configure a cxx compiler!')
def set_options(opt):
build_platform = Utils.unversioned_sys_platform()
possible_compiler_list = __list_possible_compiler(build_platform)
test_for_compiler = ' '.join(possible_compiler_list)
cxx_compiler_opts = opt.add_option_group('C++ Compiler Options')
cxx_compiler_opts.add_option('--check-cxx-compiler', default="%s" % test_for_compiler,
help='On this platform (%s) the following C++ Compiler will be checked by default: "%s"' % (build_platform, test_for_compiler),
dest="check_cxx_compiler")
for cxx_compiler in test_for_compiler.split():
opt.tool_options('%s' % cxx_compiler, option_group=cxx_compiler_opts)
#!/usr/bin/env python
# encoding: utf-8
# Carlos Rafael Giani, 2007 (dv)
import os, sys, imp, types
import Utils, Configure, Options
def detect(conf):
if getattr(Options.options, 'check_dmd_first', None):
test_for_compiler = ['dmd', 'gdc']
else:
test_for_compiler = ['gdc', 'dmd']
for d_compiler in test_for_compiler:
try:
conf.check_tool(d_compiler)
except:
pass
else:
break
else:
conf.fatal('no suitable d compiler was found')
def set_options(opt):
d_compiler_opts = opt.add_option_group('D Compiler Options')
d_compiler_opts.add_option('--check-dmd-first', action='store_true',
help='checks for the gdc compiler before dmd (default is the other way round)',
dest='check_dmd_first',
default=False)
for d_compiler in ['gdc', 'dmd']:
opt.tool_options('%s' % d_compiler, option_group=d_compiler_opts)
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2005-2008 (ita)
"""
c/c++ configuration routines
"""
import os, imp, sys, shlex, shutil
from Utils import md5
import Build, Utils, Configure, Task, Options, Logs, TaskGen
from Constants import *
from Configure import conf, conftest
cfg_ver = {
'atleast-version': '>=',
'exact-version': '==',
'max-version': '<=',
}
SNIP1 = '''
int main() {
void *p;
p=(void*)(%s);
return 0;
}
'''
SNIP2 = '''
int main() {
if ((%(type_name)s *) 0) return 0;
if (sizeof (%(type_name)s)) return 0;
}
'''
SNIP3 = '''
int main() {
return 0;
}
'''
def parse_flags(line, uselib, env):
"""pkg-config still has bugs on some platforms, and there are many -config programs, parsing flags is necessary :-/"""
lst = shlex.split(line)
while lst:
x = lst.pop(0)
st = x[:2]
ot = x[2:]
if st == '-I' or st == '/I':
if not ot: ot = lst.pop(0)
env.append_unique('CPPPATH_' + uselib, ot)
elif st == '-D':
if not ot: ot = lst.pop(0)
env.append_unique('CXXDEFINES_' + uselib, ot)
env.append_unique('CCDEFINES_' + uselib, ot)
elif st == '-l':
if not ot: ot = lst.pop(0)
env.append_unique('LIB_' + uselib, ot)
elif st == '-L':
if not ot: ot = lst.pop(0)
env.append_unique('LIBPATH_' + uselib, ot)
elif x == '-pthread' or x.startswith('+'):
env.append_unique('CCFLAGS_' + uselib, x)
env.append_unique('CXXFLAGS_' + uselib, x)
env.append_unique('LINKFLAGS_' + uselib, x)
elif x == '-framework':
env.append_unique('FRAMEWORK_' + uselib, lst.pop(0))
elif x.startswith('-F'):
env.append_unique('FRAMEWORKPATH_' + uselib, x[2:])
elif x.startswith('-std'):
env.append_unique('CCFLAGS_' + uselib, x)
env.append_unique('LINKFLAGS_' + uselib, x)
elif x.startswith('-Wl'):
env.append_unique('LINKFLAGS_' + uselib, x)
elif x.startswith('-m') or x.startswith('-f'):
env.append_unique('CCFLAGS_' + uselib, x)
env.append_unique('CXXFLAGS_' + uselib, x)
@conf
def ret_msg(self, f, kw):
"""execute a function, when provided"""
if isinstance(f, str):
return f
return f(kw)
@conf
def validate_cfg(self, kw):
if not 'path' in kw:
kw['path'] = 'pkg-config --errors-to-stdout --print-errors'
# pkg-config version
if 'atleast_pkgconfig_version' in kw:
if not 'msg' in kw:
kw['msg'] = 'Checking for pkg-config version >= %s' % kw['atleast_pkgconfig_version']
return
# pkg-config --modversion
if 'modversion' in kw:
return
if 'variables' in kw:
if not 'msg' in kw:
kw['msg'] = 'Checking for %s variables' % kw['package']
return
# checking for the version of a module, for the moment, one thing at a time
for x in cfg_ver.keys():
y = x.replace('-', '_')
if y in kw:
if not 'package' in kw:
raise ValueError('%s requires a package' % x)
if not 'msg' in kw:
kw['msg'] = 'Checking for %s %s %s' % (kw['package'], cfg_ver[x], kw[y])
return
if not 'msg' in kw:
kw['msg'] = 'Checking for %s' % (kw['package'] or kw['path'])
if not 'okmsg' in kw:
kw['okmsg'] = 'yes'
if not 'errmsg' in kw:
kw['errmsg'] = 'not found'
@conf
def cmd_and_log(self, cmd, kw):
Logs.debug('runner: %s\n' % cmd)
if self.log:
self.log.write('%s\n' % cmd)
try:
p = Utils.pproc.Popen(cmd, stdout=Utils.pproc.PIPE, stderr=Utils.pproc.PIPE, shell=True)
(out, err) = p.communicate()
except OSError, e:
self.log.write('error %r' % e)
self.fatal(str(e))
out = str(out)
err = str(err)
if self.log:
self.log.write(out)
self.log.write(err)
if p.returncode:
if not kw.get('errmsg', ''):
if kw.get('mandatory', False):
kw['errmsg'] = out.strip()
else:
kw['errmsg'] = 'no'
self.fatal('fail')
return out
@conf
def exec_cfg(self, kw):
# pkg-config version
if 'atleast_pkgconfig_version' in kw:
cmd = '%s --atleast-pkgconfig-version=%s' % (kw['path'], kw['atleast_pkgconfig_version'])
self.cmd_and_log(cmd, kw)
if not 'okmsg' in kw:
kw['okmsg'] = 'yes'
return
# checking for the version of a module
for x in cfg_ver:
y = x.replace('-', '_')
if y in kw:
self.cmd_and_log('%s --%s=%s %s' % (kw['path'], x, kw[y], kw['package']), kw)
if not 'okmsg' in kw:
kw['okmsg'] = 'yes'
self.define(self.have_define(kw.get('uselib_store', kw['package'])), 1, 0)
break
# retrieving the version of a module
if 'modversion' in kw:
version = self.cmd_and_log('%s --modversion %s' % (kw['path'], kw['modversion']), kw).strip()
self.define('%s_VERSION' % Utils.quote_define_name(kw.get('uselib_store', kw['modversion'])), version)
return version
# retrieving variables of a module
if 'variables' in kw:
env = kw.get('env', self.env)
uselib = kw.get('uselib_store', kw['package'].upper())
vars = Utils.to_list(kw['variables'])
for v in vars:
val = self.cmd_and_log('%s --variable=%s %s' % (kw['path'], v, kw['package']), kw).strip()
var = '%s_%s' % (uselib, v)
env[var] = val
if not 'okmsg' in kw:
kw['okmsg'] = 'yes'
return
lst = [kw['path']]
for key, val in kw.get('define_variable', {}).iteritems():
lst.append('--define-variable=%s=%s' % (key, val))
lst.append(kw.get('args', ''))
lst.append(kw['package'])
# so we assume the command-line will output flags to be parsed afterwards
cmd = ' '.join(lst)
ret = self.cmd_and_log(cmd, kw)
if not 'okmsg' in kw:
kw['okmsg'] = 'yes'
self.define(self.have_define(kw.get('uselib_store', kw['package'])), 1, 0)
parse_flags(ret, kw.get('uselib_store', kw['package'].upper()), kw.get('env', self.env))
return ret
@conf
def check_cfg(self, *k, **kw):
"""
for pkg-config mostly, but also all the -config tools
conf.check_cfg(path='mpicc', args='--showme:compile --showme:link', package='', uselib_store='OPEN_MPI')
conf.check_cfg(package='dbus-1', variables='system_bus_default_address session_bus_services_dir')
"""
self.validate_cfg(kw)
if 'msg' in kw:
self.check_message_1(kw['msg'])
ret = None
try:
ret = self.exec_cfg(kw)
except Configure.ConfigurationError, e:
if 'errmsg' in kw:
self.check_message_2(kw['errmsg'], 'YELLOW')
if 'mandatory' in kw and kw['mandatory']:
if Logs.verbose > 1:
raise
else:
self.fatal('the configuration failed (see %r)' % self.log.name)
else:
kw['success'] = ret
if 'okmsg' in kw:
self.check_message_2(self.ret_msg(kw['okmsg'], kw))
return ret
# the idea is the following: now that we are certain
# that all the code here is only for c or c++, it is
# easy to put all the logic in one function
#
# this should prevent code duplication (ita)
# env: an optional environment (modified -> provide a copy)
# compiler: cc or cxx - it tries to guess what is best
# type: cprogram, cshlib, cstaticlib
# code: a c code to execute
# uselib_store: where to add the variables
# uselib: parameters to use for building
# define: define to set, like FOO in #define FOO, if not set, add /* #undef FOO */
# execute: True or False - will return the result of the execution
@conf
def validate_c(self, kw):
"""validate the parameters for the test method"""
if not 'env' in kw:
kw['env'] = self.env.copy()
env = kw['env']
if not 'compiler' in kw:
kw['compiler'] = 'cc'
if env['CXX_NAME'] and Task.TaskBase.classes.get('cxx', None):
kw['compiler'] = 'cxx'
if not self.env['CXX']:
self.fatal('a c++ compiler is required')
else:
if not self.env['CC']:
self.fatal('a c compiler is required')
if not 'type' in kw:
kw['type'] = 'cprogram'
assert not(kw['type'] != 'cprogram' and kw.get('execute', 0)), 'can only execute programs'
#if kw['type'] != 'program' and kw.get('execute', 0):
# raise ValueError, 'can only execute programs'
def to_header(dct):
if 'header_name' in dct:
dct = Utils.to_list(dct['header_name'])
return ''.join(['#include <%s>\n' % x for x in dct])
return ''
# set the file name
if not 'compile_mode' in kw:
kw['compile_mode'] = (kw['compiler'] == 'cxx') and 'cxx' or 'cc'
if not 'compile_filename' in kw:
kw['compile_filename'] = 'test.c' + ((kw['compile_mode'] == 'cxx') and 'pp' or '')
#OSX
if 'framework_name' in kw:
try: TaskGen.task_gen.create_task_macapp
except AttributeError: self.fatal('frameworks require the osx tool')
fwkname = kw['framework_name']
if not 'uselib_store' in kw:
kw['uselib_store'] = fwkname.upper()
if not kw.get('no_header', False):
if not 'header_name' in kw:
kw['header_name'] = []
fwk = '%s/%s.h' % (fwkname, fwkname)
if kw.get('remove_dot_h', None):
fwk = fwk[:-2]
kw['header_name'] = Utils.to_list(kw['header_name']) + [fwk]
kw['msg'] = 'Checking for framework %s' % fwkname
kw['framework'] = fwkname
#kw['frameworkpath'] = set it yourself
if 'function_name' in kw:
fu = kw['function_name']
if not 'msg' in kw:
kw['msg'] = 'Checking for function %s' % fu
kw['code'] = to_header(kw) + SNIP1 % fu
if not 'uselib_store' in kw:
kw['uselib_store'] = fu.upper()
if not 'define_name' in kw:
kw['define_name'] = self.have_define(fu)
elif 'type_name' in kw:
tu = kw['type_name']
if not 'msg' in kw:
kw['msg'] = 'Checking for type %s' % tu
if not 'header_name' in kw:
kw['header_name'] = 'stdint.h'
kw['code'] = to_header(kw) + SNIP2 % {'type_name' : tu}
if not 'define_name' in kw:
kw['define_name'] = self.have_define(tu.upper())
elif 'header_name' in kw:
if not 'msg' in kw:
kw['msg'] = 'Checking for header %s' % kw['header_name']
l = Utils.to_list(kw['header_name'])
assert len(l)>0, 'list of headers in header_name is empty'
kw['code'] = to_header(kw) + SNIP3
if not 'uselib_store' in kw:
kw['uselib_store'] = l[0].upper()
if not 'define_name' in kw:
kw['define_name'] = self.have_define(l[0])
if 'lib' in kw:
if not 'msg' in kw:
kw['msg'] = 'Checking for library %s' % kw['lib']
if not 'uselib_store' in kw:
kw['uselib_store'] = kw['lib'].upper()
if 'staticlib' in kw:
if not 'msg' in kw:
kw['msg'] = 'Checking for static library %s' % kw['staticlib']
if not 'uselib_store' in kw:
kw['uselib_store'] = kw['staticlib'].upper()
if 'fragment' in kw:
# an additional code fragment may be provided to replace the predefined code
# in custom headers
kw['code'] = kw['fragment']
if not 'msg' in kw:
kw['msg'] = 'Checking for custom code'
if not 'errmsg' in kw:
kw['errmsg'] = 'no'
for (flagsname,flagstype) in [('cxxflags','compiler'), ('cflags','compiler'), ('linkflags','linker')]:
if flagsname in kw:
if not 'msg' in kw:
kw['msg'] = 'Checking for %s flags %s' % (flagstype, kw[flagsname])
if not 'errmsg' in kw:
kw['errmsg'] = 'no'
if not 'execute' in kw:
kw['execute'] = False
if not 'errmsg' in kw:
kw['errmsg'] = 'not found'
if not 'okmsg' in kw:
kw['okmsg'] = 'yes'
if not 'code' in kw:
kw['code'] = SNIP3
if not kw.get('success'): kw['success'] = None
assert 'msg' in kw, 'invalid parameters, read http://freehackers.org/~tnagy/wafbook/single.html#config_helpers_c'
@conf
def post_check(self, *k, **kw):
"set the variables after a test was run successfully"
is_success = False
if kw['execute']:
if kw['success']:
is_success = True
else:
is_success = (kw['success'] == 0)
if 'define_name' in kw:
if 'header_name' in kw or 'function_name' in kw or 'type_name' in kw or 'fragment' in kw:
if kw['execute']:
key = kw['success']
if isinstance(key, str):
if key:
self.define(kw['define_name'], key, quote=kw.get('quote', 1))
else:
self.define_cond(kw['define_name'], True)
else:
self.define_cond(kw['define_name'], False)
else:
self.define_cond(kw['define_name'], is_success)
if is_success and 'uselib_store' in kw:
import cc, cxx
for k in set(cc.g_cc_flag_vars).union(cxx.g_cxx_flag_vars):
lk = k.lower()
# inconsistency: includes -> CPPPATH
if k == 'CPPPATH': lk = 'includes'
if k == 'CXXDEFINES': lk = 'defines'
if k == 'CCDEFINES': lk = 'defines'
if lk in kw:
val = kw[lk]
# remove trailing slash
if isinstance(val, str):
val = val.rstrip(os.path.sep)
self.env.append_unique(k + '_' + kw['uselib_store'], val)
@conf
def check(self, *k, **kw):
# so this will be the generic function
# it will be safer to use check_cxx or check_cc
self.validate_c(kw)
self.check_message_1(kw['msg'])
ret = None
try:
ret = self.run_c_code(*k, **kw)
except Configure.ConfigurationError, e:
self.check_message_2(kw['errmsg'], 'YELLOW')
if 'mandatory' in kw and kw['mandatory']:
if Logs.verbose > 1:
raise
else:
self.fatal('the configuration failed (see %r)' % self.log.name)
else:
kw['success'] = ret
self.check_message_2(self.ret_msg(kw['okmsg'], kw))
self.post_check(*k, **kw)
if not kw.get('execute', False):
return ret == 0
return ret
@conf
def run_c_code(self, *k, **kw):
test_f_name = kw['compile_filename']
k = 0
while k < 10000:
# make certain to use a fresh folder - necessary for win32
dir = os.path.join(self.blddir, '.conf_check_%d' % k)
# if the folder already exists, remove it
try:
shutil.rmtree(dir)
except OSError:
pass
try:
os.stat(dir)
except OSError:
break
k += 1
try:
os.makedirs(dir)
except:
self.fatal('cannot create a configuration test folder %r' % dir)
try:
os.stat(dir)
except:
self.fatal('cannot use the configuration test folder %r' % dir)
bdir = os.path.join(dir, 'testbuild')
if not os.path.exists(bdir):
os.makedirs(bdir)
env = kw['env']
dest = open(os.path.join(dir, test_f_name), 'w')
dest.write(kw['code'])
dest.close()
back = os.path.abspath('.')
bld = Build.BuildContext()
bld.log = self.log
bld.all_envs.update(self.all_envs)
bld.all_envs['default'] = env
bld.lst_variants = bld.all_envs.keys()
bld.load_dirs(dir, bdir)
os.chdir(dir)
bld.rescan(bld.srcnode)
if not 'features' in kw:
# conf.check(features='cc cprogram pyext', ...)
kw['features'] = [kw['compile_mode'], kw['type']] # "cprogram cc"
o = bld(features=kw['features'], source=test_f_name, target='testprog')
for k, v in kw.iteritems():
setattr(o, k, v)
self.log.write("==>\n%s\n<==\n" % kw['code'])
# compile the program
try:
bld.compile()
except Utils.WafError:
ret = Utils.ex_stack()
else:
ret = 0
# chdir before returning
os.chdir(back)
if ret:
self.log.write('command returned %r' % ret)
self.fatal(str(ret))
# if we need to run the program, try to get its result
# keep the name of the program to execute
if kw['execute']:
lastprog = o.link_task.outputs[0].abspath(env)
args = Utils.to_list(kw.get('exec_args', []))
proc = Utils.pproc.Popen([lastprog] + args, stdout=Utils.pproc.PIPE, stderr=Utils.pproc.PIPE)
(out, err) = proc.communicate()
w = self.log.write
w(str(out))
w('\n')
w(str(err))
w('\n')
w('returncode %r' % proc.returncode)
w('\n')
if proc.returncode:
self.fatal(Utils.ex_stack())
ret = out
return ret
@conf
def check_cxx(self, *k, **kw):
kw['compiler'] = 'cxx'
return self.check(*k, **kw)
@conf
def check_cc(self, *k, **kw):
kw['compiler'] = 'cc'
return self.check(*k, **kw)
@conf
def define(self, define, value, quote=1):
"""store a single define and its state into an internal list for later
writing to a config header file. Value can only be
a string or int; other types not supported. String
values will appear properly quoted in the generated
header file."""
assert define and isinstance(define, str)
# ordered_dict is for writing the configuration header in order
tbl = self.env[DEFINES] or Utils.ordered_dict()
# the user forgot to tell if the value is quoted or not
if isinstance(value, str):
if quote:
tbl[define] = '"%s"' % repr('"'+value)[2:-1].replace('"', '\\"')
else:
tbl[define] = value
elif isinstance(value, int):
tbl[define] = value
else:
raise TypeError('define %r -> %r must be a string or an int' % (define, value))
# add later to make reconfiguring faster
self.env[DEFINES] = tbl
self.env[define] = value # <- not certain this is necessary
@conf
def undefine(self, define):
"""store a single define and its state into an internal list
for later writing to a config header file"""
assert define and isinstance(define, str)
tbl = self.env[DEFINES] or Utils.ordered_dict()
value = UNDEFINED
tbl[define] = value
# add later to make reconfiguring faster
self.env[DEFINES] = tbl
self.env[define] = value
@conf
def define_cond(self, name, value):
"""Conditionally define a name.
Formally equivalent to: if value: define(name, 1) else: undefine(name)"""
if value:
self.define(name, 1)
else:
self.undefine(name)
@conf
def is_defined(self, key):
defines = self.env[DEFINES]
if not defines:
return False
try:
value = defines[key]
except KeyError:
return False
else:
return value != UNDEFINED
@conf
def get_define(self, define):
"get the value of a previously stored define"
try: return self.env[DEFINES][define]
except KeyError: return None
@conf
def have_define(self, name):
"prefix the define with 'HAVE_' and make sure it has valid characters."
return self.__dict__.get('HAVE_PAT', 'HAVE_%s') % Utils.quote_define_name(name)
@conf
def write_config_header(self, configfile='', env='', guard='', top=False):
"save the defines into a file"
if not configfile: configfile = WAF_CONFIG_H
waf_guard = guard or '_%s_WAF' % Utils.quote_define_name(configfile)
# configfile -> absolute path
# there is a good reason to concatenate first and to split afterwards
if not env: env = self.env
if top:
diff = ''
else:
diff = Utils.diff_path(self.srcdir, self.curdir)
full = os.sep.join([self.blddir, env.variant(), diff, configfile])
full = os.path.normpath(full)
(dir, base) = os.path.split(full)
try: os.makedirs(dir)
except: pass
dest = open(full, 'w')
dest.write('/* Configuration header created by Waf - do not edit */\n')
dest.write('#ifndef %s\n#define %s\n\n' % (waf_guard, waf_guard))
dest.write(self.get_config_header())
# config files are not removed on "waf clean"
env.append_unique(CFG_FILES, os.path.join(diff, configfile))
dest.write('\n#endif /* %s */\n' % waf_guard)
dest.close()
@conf
def get_config_header(self):
"""Fill-in the contents of the config header. Override when you need to write your own config header."""
config_header = []
tbl = self.env[DEFINES] or Utils.ordered_dict()
for key in tbl.allkeys:
value = tbl[key]
if value is None:
config_header.append('#define %s' % key)
elif value is UNDEFINED:
config_header.append('/* #undef %s */' % key)
else:
config_header.append('#define %s %s' % (key, value))
return "\n".join(config_header)
@conftest
def find_cpp(conf):
v = conf.env
cpp = None
if v['CPP']: cpp = v['CPP']
elif 'CPP' in conf.environ: cpp = conf.environ['CPP']
if not cpp: cpp = conf.find_program('cpp', var='CPP')
if not cpp: cpp = v['CC']
if not cpp: cpp = v['CXX']
v['CPP'] = cpp
@conftest
def cc_add_flags(conf):
conf.add_os_flags('CFLAGS', 'CCFLAGS')
conf.add_os_flags('CPPFLAGS')
@conftest
def cxx_add_flags(conf):
conf.add_os_flags('CXXFLAGS')
conf.add_os_flags('CPPFLAGS')
@conftest
def link_add_flags(conf):
conf.add_os_flags('LINKFLAGS')
conf.add_os_flags('LDFLAGS', 'LINKFLAGS')
@conftest
def cc_load_tools(conf):
conf.check_tool('cc')
@conftest
def cxx_load_tools(conf):
conf.check_tool('cxx')
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2005 (ita)
"Base for c++ programs and libraries"
import TaskGen, Task, Utils
from Logs import debug
import ccroot # <- do not remove
from TaskGen import feature, before, extension, after
g_cxx_flag_vars = [
'CXXDEPS', 'FRAMEWORK', 'FRAMEWORKPATH',
'STATICLIB', 'LIB', 'LIBPATH', 'LINKFLAGS', 'RPATH',
'CXXFLAGS', 'CCFLAGS', 'CPPPATH', 'CPPFLAGS', 'CXXDEFINES']
"main cpp variables"
EXT_CXX = ['.cpp', '.cc', '.cxx', '.C', '.c++']
g_cxx_type_vars=['CXXFLAGS', 'LINKFLAGS']
# TODO remove in waf 1.6
class cxx_taskgen(ccroot.ccroot_abstract):
pass
@feature('cxx')
@before('apply_type_vars')
@after('default_cc')
def init_cxx(self):
if not 'cc' in self.features:
self.mappings['.c'] = TaskGen.task_gen.mappings['.cxx']
self.p_flag_vars = set(self.p_flag_vars).union(g_cxx_flag_vars)
self.p_type_vars = set(self.p_type_vars).union(g_cxx_type_vars)
if not self.env['CXX_NAME']:
raise Utils.WafError("At least one compiler (g++, ..) must be selected")
@feature('cxx')
@after('apply_incpaths')
def apply_obj_vars_cxx(self):
"""after apply_incpaths for INC_PATHS"""
env = self.env
app = env.append_unique
cxxpath_st = env['CPPPATH_ST']
# local flags come first
# set the user-defined includes paths
for i in env['INC_PATHS']:
app('_CXXINCFLAGS', cxxpath_st % i.bldpath(env))
app('_CXXINCFLAGS', cxxpath_st % i.srcpath(env))
# set the library include paths
for i in env['CPPPATH']:
app('_CXXINCFLAGS', cxxpath_st % i)
@feature('cxx')
@after('apply_lib_vars')
def apply_defines_cxx(self):
"""after uselib is set for CXXDEFINES"""
self.defines = getattr(self, 'defines', [])
lst = self.to_list(self.defines) + self.to_list(self.env['CXXDEFINES'])
milst = []
# now process the local defines
for defi in lst:
if not defi in milst:
milst.append(defi)
# CXXDEFINES_USELIB
libs = self.to_list(self.uselib)
for l in libs:
val = self.env['CXXDEFINES_'+l]
if val: milst += self.to_list(val)
self.env['DEFLINES'] = ["%s %s" % (x[0], Utils.trimquotes('='.join(x[1:]))) for x in [y.split('=') for y in milst]]
y = self.env['CXXDEFINES_ST']
self.env['_CXXDEFFLAGS'] = [y%x for x in milst]
@extension(EXT_CXX)
def cxx_hook(self, node):
# create the compilation task: cpp or cc
if getattr(self, 'obj_ext', None):
obj_ext = self.obj_ext
else:
obj_ext = '_%d.o' % self.idx
task = self.create_task('cxx', node, node.change_ext(obj_ext))
try:
self.compiled_tasks.append(task)
except AttributeError:
raise Utils.WafError('Have you forgotten to set the feature "cxx" on %s?' % str(self))
return task
cxx_str = '${CXX} ${CXXFLAGS} ${CPPFLAGS} ${_CXXINCFLAGS} ${_CXXDEFFLAGS} ${CXX_SRC_F}${SRC} ${CXX_TGT_F}${TGT}'
cls = Task.simple_task_type('cxx', cxx_str, color='GREEN', ext_out='.o', ext_in='.cxx', shell=False)
cls.scan = ccroot.scan
cls.vars.append('CXXDEPS')
link_str = '${LINK_CXX} ${CXXLNK_SRC_F}${SRC} ${CXXLNK_TGT_F}${TGT[0].abspath(env)} ${LINKFLAGS}'
cls = Task.simple_task_type('cxx_link', link_str, color='YELLOW', ext_in='.o', ext_out='.bin', shell=False)
cls.maxjobs = 1
cls.install = Utils.nada
#!/usr/bin/env python
# encoding: utf-8
# Carlos Rafael Giani, 2007 (dv)
# Thomas Nagy, 2007-2008 (ita)
import os, sys, re, optparse
import ccroot # <- leave this
import TaskGen, Utils, Task, Configure, Logs, Build
from Logs import debug, error
from TaskGen import taskgen, feature, after, before, extension
from Configure import conftest
EXT_D = ['.d', '.di', '.D']
D_METHS = ['apply_core', 'apply_vnum', 'apply_objdeps'] # additional d methods
def filter_comments(filename):
txt = Utils.readf(filename)
buf = []
i = 0
max = len(txt)
while i < max:
c = txt[i]
# skip a string
if c == '"':
i += 1
c = ''
while i < max:
p = c
c = txt[i]
i += 1
if i == max: return buf
if c == '"':
cnt = 0
while i < cnt and i < max:
#print "cntcnt = ", str(cnt), self.txt[self.i-2-cnt]
if txt[i-2-cnt] == '\\': cnt+=1
else: break
#print "cnt is ", str(cnt)
if (cnt%2)==0: break
i += 1
# skip a char
elif c == "'":
i += 1
if i == max: return buf
c = txt[i]
if c == '\\':
i += 1
if i == max: return buf
c = txt[i]
if c == 'x':
i += 2 # skip two chars
elif c == 'u':
i += 4 # skip unicode chars
i += 1
if i == max: return buf
c = txt[i]
if c != '\'': error("uh-oh, invalid character")
# skip a comment
elif c == '/':
if i == max: break
c = txt[i+1]
# eat /+ +/ comments
if c == '+':
i += 1
nesting = 1
prev = 0
while i < max:
c = txt[i]
if c == '+':
prev = 1
elif c == '/':
if prev:
nesting -= 1
if nesting == 0: break
else:
if i < max:
i += 1
c = txt[i]
if c == '+':
nesting += 1
else:
return buf
else:
prev = 0
i += 1
# eat /* */ comments
elif c == '*':
i += 1
while i < max:
c = txt[i]
if c == '*':
prev = 1
elif c == '/':
if prev: break
else:
prev = 0
i += 1
# eat // comments
elif c == '/':
i += 1
c = txt[i]
while i < max and c != '\n':
i += 1
c = txt[i]
# a valid char, add it to the buffer
else:
buf.append(c)
i += 1
return buf
class d_parser(object):
def __init__(self, env, incpaths):
#self.code = ''
#self.module = ''
#self.imports = []
self.allnames = []
self.re_module = re.compile("module\s+([^;]+)")
self.re_import = re.compile("import\s+([^;]+)")
self.re_import_bindings = re.compile("([^:]+):(.*)")
self.re_import_alias = re.compile("[^=]+=(.+)")
self.env = env
self.nodes = []
self.names = []
self.incpaths = incpaths
def tryfind(self, filename):
found = 0
for n in self.incpaths:
found = n.find_resource(filename.replace('.', '/') + '.d')
if found:
self.nodes.append(found)
self.waiting.append(found)
break
if not found:
if not filename in self.names:
self.names.append(filename)
def get_strings(self, code):
#self.imports = []
self.module = ''
lst = []
# get the module name (if present)
mod_name = self.re_module.search(code)
if mod_name:
self.module = re.sub('\s+', '', mod_name.group(1)) # strip all whitespaces
# go through the code, have a look at all import occurrences
# first, lets look at anything beginning with "import" and ending with ";"
import_iterator = self.re_import.finditer(code)
if import_iterator:
for import_match in import_iterator:
import_match_str = re.sub('\s+', '', import_match.group(1)) # strip all whitespaces
# does this end with an import bindings declaration?
# (import bindings always terminate the list of imports)
bindings_match = self.re_import_bindings.match(import_match_str)
if bindings_match:
import_match_str = bindings_match.group(1)
# if so, extract the part before the ":" (since the module declaration(s) is/are located there)
# split the matching string into a bunch of strings, separated by a comma
matches = import_match_str.split(',')
for match in matches:
alias_match = self.re_import_alias.match(match)
if alias_match:
# is this an alias declaration? (alias = module name) if so, extract the module name
match = alias_match.group(1)
lst.append(match)
return lst
def start(self, node):
self.waiting = [node]
# while the stack is not empty, add the dependencies
while self.waiting:
nd = self.waiting.pop(0)
self.iter(nd)
def iter(self, node):
path = node.abspath(self.env) # obtain the absolute path
code = "".join(filter_comments(path)) # read the file and filter the comments
names = self.get_strings(code) # obtain the import strings
for x in names:
# optimization
if x in self.allnames: continue
self.allnames.append(x)
# for each name, see if it is like a node or not
self.tryfind(x)
def scan(self):
"look for .d/.di the .d source need"
env = self.env
gruik = d_parser(env, env['INC_PATHS'])
gruik.start(self.inputs[0])
if Logs.verbose:
debug('deps: nodes found for %s: %s %s' % (str(self.inputs[0]), str(gruik.nodes), str(gruik.names)))
#debug("deps found for %s: %s" % (str(node), str(gruik.deps)), 'deps')
return (gruik.nodes, gruik.names)
def get_target_name(self):
"for d programs and libs"
v = self.env
tp = 'program'
for x in self.features:
if x in ['dshlib', 'dstaticlib']:
tp = x.lstrip('d')
return v['D_%s_PATTERN' % tp] % self.target
d_params = {
'dflags': '',
'importpaths':'',
'libs':'',
'libpaths':'',
'generate_headers':False,
}
@feature('d')
@before('apply_type_vars')
def init_d(self):
for x in d_params:
setattr(self, x, getattr(self, x, d_params[x]))
class d_taskgen(TaskGen.task_gen):
def __init__(self, *k, **kw):
TaskGen.task_gen.__init__(self, *k, **kw)
# COMPAT
if len(k) > 1:
self.features.append('d' + k[1])
# okay, we borrow a few methods from ccroot
TaskGen.bind_feature('d', D_METHS)
@feature('d')
@before('apply_d_libs')
def init_d(self):
Utils.def_attrs(self,
dflags='',
importpaths='',
libs='',
libpaths='',
uselib='',
uselib_local='',
generate_headers=False, # set to true if you want .di files as well as .o
compiled_tasks=[],
add_objects=[],
link_task=None)
@feature('d')
@after('apply_d_link', 'init_d')
@before('apply_vnum')
def apply_d_libs(self):
"""after apply_link because of 'link_task'
after default_cc because of the attribute 'uselib'"""
env = self.env
# 1. the case of the libs defined in the project (visit ancestors first)
# the ancestors external libraries (uselib) will be prepended
self.uselib = self.to_list(self.uselib)
names = self.to_list(self.uselib_local)
seen = set([])
tmp = Utils.deque(names) # consume a copy of the list of names
while tmp:
lib_name = tmp.popleft()
# visit dependencies only once
if lib_name in seen:
continue
y = self.name_to_obj(lib_name)
if not y:
raise Utils.WafError('object %r was not found in uselib_local (required by %r)' % (lib_name, self.name))
y.post()
seen.add(lib_name)
# object has ancestors to process (shared libraries): add them to the end of the list
if getattr(y, 'uselib_local', None):
lst = y.to_list(y.uselib_local)
if 'dshlib' in y.features or 'dprogram' in y.features:
lst = [x for x in lst if not 'dstaticlib' in self.name_to_obj(x).features]
tmp.extend(lst)
# link task and flags
if getattr(y, 'link_task', None):
link_name = y.target[y.target.rfind(os.sep) + 1:]
if 'dstaticlib' in y.features or 'dshlib' in y.features:
env.append_unique('DLINKFLAGS', env.DLIB_ST % link_name)
env.append_unique('DLINKFLAGS', env.DLIBPATH_ST % y.link_task.outputs[0].parent.bldpath(env))
# the order
self.link_task.set_run_after(y.link_task)
# for the recompilation
dep_nodes = getattr(self.link_task, 'dep_nodes', [])
self.link_task.dep_nodes = dep_nodes + y.link_task.outputs
# add ancestors uselib too - but only propagate those that have no staticlib
for v in self.to_list(y.uselib):
if not v in self.uselib:
self.uselib.insert(0, v)
# if the library task generator provides 'export_incdirs', add to the include path
# the export_incdirs must be a list of paths relative to the other library
if getattr(y, 'export_incdirs', None):
for x in self.to_list(y.export_incdirs):
node = y.path.find_dir(x)
if not node:
raise Utils.WafError('object %r: invalid folder %r in export_incdirs' % (y.target, x))
self.env.append_unique('INC_PATHS', node)
@feature('dprogram', 'dshlib', 'dstaticlib')
@after('apply_core')
def apply_d_link(self):
link = getattr(self, 'link', None)
if not link:
if 'dstaticlib' in self.features: link = 'static_link'
else: link = 'd_link'
outputs = [t.outputs[0] for t in self.compiled_tasks]
self.link_task = self.create_task(link, outputs, self.path.find_or_declare(get_target_name(self)))
@feature('d')
@after('apply_core')
def apply_d_vars(self):
env = self.env
dpath_st = env['DPATH_ST']
lib_st = env['DLIB_ST']
libpath_st = env['DLIBPATH_ST']
importpaths = self.to_list(self.importpaths)
libpaths = []
libs = []
uselib = self.to_list(self.uselib)
for i in uselib:
if env['DFLAGS_' + i]:
env.append_unique('DFLAGS', env['DFLAGS_' + i])
for x in self.features:
if not x in ['dprogram', 'dstaticlib', 'dshlib']:
continue
x.lstrip('d')
d_shlib_dflags = env['D_' + x + '_DFLAGS']
if d_shlib_dflags:
env.append_unique('DFLAGS', d_shlib_dflags)
# add import paths
for i in uselib:
if env['DPATH_' + i]:
for entry in self.to_list(env['DPATH_' + i]):
if not entry in importpaths:
importpaths.append(entry)
# now process the import paths
for path in importpaths:
if os.path.isabs(path):
env.append_unique('_DIMPORTFLAGS', dpath_st % path)
else:
node = self.path.find_dir(path)
self.env.append_unique('INC_PATHS', node)
env.append_unique('_DIMPORTFLAGS', dpath_st % node.srcpath(env))
env.append_unique('_DIMPORTFLAGS', dpath_st % node.bldpath(env))
# add library paths
for i in uselib:
if env['LIBPATH_' + i]:
for entry in self.to_list(env['LIBPATH_' + i]):
if not entry in libpaths:
libpaths.append(entry)
libpaths = self.to_list(self.libpaths) + libpaths
# now process the library paths
# apply same path manipulation as used with import paths
for path in libpaths:
if not os.path.isabs(path):
node = self.path.find_resource(path)
if not node:
raise Utils.WafError('could not find libpath %r from %r' % (path, self))
path = node.abspath(self.env)
env.append_unique('DLINKFLAGS', libpath_st % path)
# add libraries
for i in uselib:
if env['LIB_' + i]:
for entry in self.to_list(env['LIB_' + i]):
if not entry in libs:
libs.append(entry)
libs.extend(self.to_list(self.libs))
# process user flags
for flag in self.to_list(self.dflags):
env.append_unique('DFLAGS', flag)
# now process the libraries
for lib in libs:
env.append_unique('DLINKFLAGS', lib_st % lib)
# add linker flags
for i in uselib:
dlinkflags = env['DLINKFLAGS_' + i]
if dlinkflags:
for linkflag in dlinkflags:
env.append_unique('DLINKFLAGS', linkflag)
@feature('dshlib')
@after('apply_d_vars')
def add_shlib_d_flags(self):
for linkflag in self.env['D_shlib_LINKFLAGS']:
self.env.append_unique('DLINKFLAGS', linkflag)
@extension(EXT_D)
def d_hook(self, node):
# create the compilation task: cpp or cc
task = self.create_task(self.generate_headers and 'd_with_header' or 'd')
try: obj_ext = self.obj_ext
except AttributeError: obj_ext = '_%d.o' % self.idx
task.inputs = [node]
task.outputs = [node.change_ext(obj_ext)]
self.compiled_tasks.append(task)
if self.generate_headers:
header_node = node.change_ext(self.env['DHEADER_ext'])
task.outputs += [header_node]
d_str = '${D_COMPILER} ${DFLAGS} ${_DIMPORTFLAGS} ${D_SRC_F}${SRC} ${D_TGT_F}${TGT}'
d_with_header_str = '${D_COMPILER} ${DFLAGS} ${_DIMPORTFLAGS} \
${D_HDR_F}${TGT[1].bldpath(env)} \
${D_SRC_F}${SRC} \
${D_TGT_F}${TGT[0].bldpath(env)}'
link_str = '${D_LINKER} ${DLNK_SRC_F}${SRC} ${DLNK_TGT_F}${TGT} ${DLINKFLAGS}'
def override_exec(cls):
"""stupid dmd wants -of stuck to the file name"""
old_exec = cls.exec_command
def exec_command(self, *k, **kw):
if isinstance(k[0], list):
lst = k[0]
for i in xrange(len(lst)):
if lst[i] == '-of':
del lst[i]
lst[i] = '-of' + lst[i]
break
return old_exec(self, *k, **kw)
cls.exec_command = exec_command
cls = Task.simple_task_type('d', d_str, 'GREEN', before='static_link d_link', shell=False)
cls.scan = scan
override_exec(cls)
cls = Task.simple_task_type('d_with_header', d_with_header_str, 'GREEN', before='static_link d_link', shell=False)
override_exec(cls)
cls = Task.simple_task_type('d_link', link_str, color='YELLOW', shell=False)
override_exec(cls)
# for feature request #104
@taskgen
def generate_header(self, filename, install_path):
if not hasattr(self, 'header_lst'): self.header_lst = []
self.meths.append('process_header')
self.header_lst.append([filename, install_path])
@before('apply_core')
def process_header(self):
env = self.env
for i in getattr(self, 'header_lst', []):
node = self.path.find_resource(i[0])
if not node:
raise Utils.WafError('file not found on d obj '+i[0])
task = self.create_task('d_header')
task.set_inputs(node)
task.set_outputs(node.change_ext('.di'))
d_header_str = '${D_COMPILER} ${D_HEADER} ${SRC}'
Task.simple_task_type('d_header', d_header_str, color='BLUE', shell=False)
@conftest
def d_platform_flags(conf):
v = conf.env
binfmt = v.DEST_BINFMT or Utils.unversioned_sys_platform_to_binary_format(
v.DEST_OS or Utils.unversioned_sys_platform())
if binfmt == 'pe':
v['D_program_PATTERN'] = '%s.exe'
v['D_shlib_PATTERN'] = 'lib%s.dll'
v['D_staticlib_PATTERN'] = 'lib%s.a'
else:
v['D_program_PATTERN'] = '%s'
v['D_shlib_PATTERN'] = 'lib%s.so'
v['D_staticlib_PATTERN'] = 'lib%s.a'
# quick test #
if __name__ == "__main__":
#Logs.verbose = 2
try: arg = sys.argv[1]
except IndexError: arg = "file.d"
print("".join(filter_comments(arg)))
# TODO
paths = ['.']
#gruik = filter()
#gruik.start(arg)
#code = "".join(gruik.buf)
#print "we have found the following code"
#print code
#print "now parsing"
#print "-------------------------------------------"
"""
parser_ = d_parser()
parser_.start(arg)
print "module: %s" % parser_.module
print "imports: ",
for imp in parser_.imports:
print imp + " ",
print
"""
#!/usr/bin/env python
# encoding: utf-8
# Carlos Rafael Giani, 2007 (dv)
# Thomas Nagy, 2008 (ita)
import sys
import Utils, ar
from Configure import conftest
@conftest
def find_dmd(conf):
conf.find_program(['dmd', 'ldc'], var='D_COMPILER', mandatory=True)
@conftest
def common_flags_ldc(conf):
v = conf.env
v['DFLAGS'] = ['-d-version=Posix']
v['DLINKFLAGS'] = []
v['D_shlib_DFLAGS'] = ['-relocation-model=pic']
@conftest
def common_flags_dmd(conf):
v = conf.env
# _DFLAGS _DIMPORTFLAGS
# Compiler is dmd so 'gdc' part will be ignored, just
# ensure key is there, so wscript can append flags to it
v['DFLAGS'] = ['-version=Posix']
v['D_SRC_F'] = ''
v['D_TGT_F'] = ['-c', '-of']
v['DPATH_ST'] = '-I%s' # template for adding import paths
# linker
v['D_LINKER'] = v['D_COMPILER']
v['DLNK_SRC_F'] = ''
v['DLNK_TGT_F'] = '-of'
v['DLIB_ST'] = '-L-l%s' # template for adding libs
v['DLIBPATH_ST'] = '-L-L%s' # template for adding libpaths
# linker debug levels
v['DFLAGS_OPTIMIZED'] = ['-O']
v['DFLAGS_DEBUG'] = ['-g', '-debug']
v['DFLAGS_ULTRADEBUG'] = ['-g', '-debug']
v['DLINKFLAGS'] = ['-quiet']
v['D_shlib_DFLAGS'] = ['-fPIC']
v['D_shlib_LINKFLAGS'] = ['-L-shared']
v['DHEADER_ext'] = '.di'
v['D_HDR_F'] = ['-H', '-Hf']
def detect(conf):
conf.find_dmd()
conf.check_tool('ar')
conf.check_tool('d')
conf.common_flags_dmd()
conf.d_platform_flags()
if conf.env.D_COMPILER.find('ldc') > -1:
conf.common_flags_ldc()
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2008 (ita)
"as and gas"
import os, sys
import Task
from TaskGen import extension, taskgen, after, before
EXT_ASM = ['.s', '.S', '.asm', '.ASM', '.spp', '.SPP']
as_str = '${AS} ${ASFLAGS} ${_ASINCFLAGS} ${SRC} -o ${TGT}'
Task.simple_task_type('asm', as_str, 'PINK', ext_out='.o', shell=False)
@extension(EXT_ASM)
def asm_hook(self, node):
# create the compilation task: cpp or cc
try: obj_ext = self.obj_ext
except AttributeError: obj_ext = '_%d.o' % self.idx
task = self.create_task('asm', node, node.change_ext(obj_ext))
self.compiled_tasks.append(task)
self.meths.append('asm_incflags')
@after('apply_obj_vars_cc')
@after('apply_obj_vars_cxx')
@before('apply_link')
def asm_incflags(self):
self.env.append_value('_ASINCFLAGS', self.env.ASINCFLAGS)
var = ('cxx' in self.features) and 'CXX' or 'CC'
self.env.append_value('_ASINCFLAGS', self.env['_%sINCFLAGS' % var])
def detect(conf):
conf.find_program(['gas', 'as'], var='AS')
if not conf.env.AS: conf.env.AS = conf.env.CC
#conf.env.ASFLAGS = ['-c'] <- may be necesary for .S files
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2006-2008 (ita)
# Ralf Habacker, 2006 (rh)
# Yinon Ehrlich, 2009
import os, sys
import Configure, Options, Utils
import ccroot, ar
from Configure import conftest
@conftest
def find_gcc(conf):
cc = conf.find_program(['gcc', 'cc'], var='CC', mandatory=True)
cc = conf.cmd_to_list(cc)
ccroot.get_cc_version(conf, cc, gcc=True)
conf.env.CC_NAME = 'gcc'
conf.env.CC = cc
@conftest
def gcc_common_flags(conf):
v = conf.env
# CPPFLAGS CCDEFINES _CCINCFLAGS _CCDEFFLAGS
v['CCFLAGS_DEBUG'] = ['-g']
v['CCFLAGS_RELEASE'] = ['-O2']
v['CC_SRC_F'] = ''
v['CC_TGT_F'] = ['-c', '-o', ''] # shell hack for -MD
v['CPPPATH_ST'] = '-I%s' # template for adding include paths
# linker
if not v['LINK_CC']: v['LINK_CC'] = v['CC']
v['CCLNK_SRC_F'] = ''
v['CCLNK_TGT_F'] = ['-o', ''] # shell hack for -MD
v['LIB_ST'] = '-l%s' # template for adding libs
v['LIBPATH_ST'] = '-L%s' # template for adding libpaths
v['STATICLIB_ST'] = '-l%s'
v['STATICLIBPATH_ST'] = '-L%s'
v['RPATH_ST'] = '-Wl,-rpath,%s'
v['CCDEFINES_ST'] = '-D%s'
v['SONAME_ST'] = '-Wl,-h,%s'
v['SHLIB_MARKER'] = '-Wl,-Bdynamic'
v['STATICLIB_MARKER'] = '-Wl,-Bstatic'
v['FULLSTATIC_MARKER'] = '-static'
# program
v['program_PATTERN'] = '%s'
# shared library
v['shlib_CCFLAGS'] = ['-fPIC', '-DPIC'] # avoid using -DPIC, -fPIC aleady defines the __PIC__ macro
v['shlib_LINKFLAGS'] = ['-shared']
v['shlib_PATTERN'] = 'lib%s.so'
# static lib
v['staticlib_LINKFLAGS'] = ['-Wl,-Bstatic']
v['staticlib_PATTERN'] = 'lib%s.a'
# osx stuff
v['LINKFLAGS_MACBUNDLE'] = ['-bundle', '-undefined', 'dynamic_lookup']
v['CCFLAGS_MACBUNDLE'] = ['-fPIC']
v['macbundle_PATTERN'] = '%s.bundle'
@conftest
def gcc_modifier_win32(conf):
v = conf.env
v['program_PATTERN'] = '%s.exe'
v['shlib_PATTERN'] = '%s.dll'
v['implib_PATTERN'] = 'lib%s.dll.a'
v['IMPLIB_ST'] = '-Wl,--out-implib,%s'
dest_arch = v['DEST_CPU']
if dest_arch == 'x86':
# On 32-bit x86, gcc emits a message telling the -fPIC option is ignored on this arch, so we remove that flag.
v['shlib_CCFLAGS'] = ['-DPIC'] # TODO this is a wrong define, we don't use -fPIC!
v.append_value('shlib_CCFLAGS', '-DDLL_EXPORT') # TODO adding nonstandard defines like this DLL_EXPORT is not a good idea
# Auto-import is enabled by default even without this option,
# but enabling it explicitly has the nice effect of suppressing the rather boring, debug-level messages
# that the linker emits otherwise.
v.append_value('LINKFLAGS', '-Wl,--enable-auto-import')
@conftest
def gcc_modifier_cygwin(conf):
gcc_modifier_win32(conf)
v = conf.env
v['shlib_PATTERN'] = 'cyg%s.dll'
v.append_value('shlib_LINKFLAGS', '-Wl,--enable-auto-image-base')
@conftest
def gcc_modifier_darwin(conf):
v = conf.env
v['shlib_CCFLAGS'] = ['-fPIC', '-compatibility_version', '1', '-current_version', '1']
v['shlib_LINKFLAGS'] = ['-dynamiclib']
v['shlib_PATTERN'] = 'lib%s.dylib'
v['staticlib_LINKFLAGS'] = []
v['SHLIB_MARKER'] = ''
v['STATICLIB_MARKER'] = ''
v['SONAME_ST'] = ''
@conftest
def gcc_modifier_aix(conf):
v = conf.env
v['program_LINKFLAGS'] = ['-Wl,-brtl']
v['shlib_LINKFLAGS'] = ['-shared','-Wl,-brtl,-bexpfull']
v['SHLIB_MARKER'] = ''
@conftest
def gcc_modifier_platform(conf):
# * set configurations specific for a platform.
# * the destination platform is detected automatically by looking at the macros the compiler predefines,
# and if it's not recognised, it fallbacks to sys.platform.
dest_os = conf.env['DEST_OS'] or Utils.unversioned_sys_platform()
gcc_modifier_func = globals().get('gcc_modifier_' + dest_os)
if gcc_modifier_func:
gcc_modifier_func(conf)
def detect(conf):
conf.find_gcc()
conf.find_cpp()
conf.find_ar()
conf.gcc_common_flags()
conf.gcc_modifier_platform()
conf.cc_load_tools()
conf.cc_add_flags()
conf.link_add_flags()
#!/usr/bin/env python
# encoding: utf-8
# Carlos Rafael Giani, 2007 (dv)
import sys
import Utils, ar
from Configure import conftest
@conftest
def find_gdc(conf):
conf.find_program('gdc', var='D_COMPILER', mandatory=True)
@conftest
def common_flags_gdc(conf):
v = conf.env
# _DFLAGS _DIMPORTFLAGS
# for mory info about the meaning of this dict see dmd.py
v['DFLAGS'] = []
v['D_SRC_F'] = ''
v['D_TGT_F'] = ['-c', '-o', '']
v['DPATH_ST'] = '-I%s' # template for adding import paths
# linker
v['D_LINKER'] = v['D_COMPILER']
v['DLNK_SRC_F'] = ''
v['DLNK_TGT_F'] = ['-o', '']
v['DLIB_ST'] = '-l%s' # template for adding libs
v['DLIBPATH_ST'] = '-L%s' # template for adding libpaths
# debug levels
v['DLINKFLAGS'] = []
v['DFLAGS_OPTIMIZED'] = ['-O3']
v['DFLAGS_DEBUG'] = ['-O0']
v['DFLAGS_ULTRADEBUG'] = ['-O0']
v['D_shlib_DFLAGS'] = []
v['D_shlib_LINKFLAGS'] = ['-shared']
v['DHEADER_ext'] = '.di'
v['D_HDR_F'] = '-fintfc -fintfc-file='
def detect(conf):
conf.find_gdc()
conf.check_tool('ar')
conf.check_tool('d')
conf.common_flags_gdc()
conf.d_platform_flags()
#!/usr/bin/env python
# encoding: utf-8
# Ali Sabil, 2007
"""
To use this module do not forget to call
opt.tool_options('gnu_dirs')
AND
conf.check_tool('gnu_dirs')
Add options for the standard GNU directories, this tool will add the options
found in autotools, and will update the environment with the following
installation variables:
* PREFIX : architecture-independent files [/usr/local]
* EXEC_PREFIX : architecture-dependent files [PREFIX]
* BINDIR : user executables [EXEC_PREFIX/bin]
* SBINDIR : user executables [EXEC_PREFIX/sbin]
* LIBEXECDIR : program executables [EXEC_PREFIX/libexec]
* SYSCONFDIR : read-only single-machine data [PREFIX/etc]
* SHAREDSTATEDIR : modifiable architecture-independent data [PREFIX/com]
* LOCALSTATEDIR : modifiable single-machine data [PREFIX/var]
* LIBDIR : object code libraries [EXEC_PREFIX/lib]
* INCLUDEDIR : C header files [PREFIX/include]
* OLDINCLUDEDIR : C header files for non-gcc [/usr/include]
* DATAROOTDIR : read-only arch.-independent data root [PREFIX/share]
* DATADIR : read-only architecture-independent data [DATAROOTDIR]
* INFODIR : info documentation [DATAROOTDIR/info]
* LOCALEDIR : locale-dependent data [DATAROOTDIR/locale]
* MANDIR : man documentation [DATAROOTDIR/man]
* DOCDIR : documentation root [DATAROOTDIR/doc/telepathy-glib]
* HTMLDIR : html documentation [DOCDIR]
* DVIDIR : dvi documentation [DOCDIR]
* PDFDIR : pdf documentation [DOCDIR]
* PSDIR : ps documentation [DOCDIR]
"""
import Utils, Options
_options = [x.split(', ') for x in '''
bindir, user executables, ${EXEC_PREFIX}/bin
sbindir, system admin executables, ${EXEC_PREFIX}/sbin
libexecdir, program executables, ${EXEC_PREFIX}/libexec
sysconfdir, read-only single-machine data, ${PREFIX}/etc
sharedstatedir, modifiable architecture-independent data, ${PREFIX}/com
localstatedir, modifiable single-machine data, ${PREFIX}/var
libdir, object code libraries, ${EXEC_PREFIX}/lib
includedir, C header files, ${PREFIX}/include
oldincludedir, C header files for non-gcc, /usr/include
datarootdir, read-only arch.-independent data root, ${PREFIX}/share
datadir, read-only architecture-independent data, ${DATAROOTDIR}
infodir, info documentation, ${DATAROOTDIR}/info
localedir, locale-dependent data, ${DATAROOTDIR}/locale
mandir, man documentation, ${DATAROOTDIR}/man
docdir, documentation root, ${DATAROOTDIR}/doc/${PACKAGE}
htmldir, html documentation, ${DOCDIR}
dvidir, dvi documentation, ${DOCDIR}
pdfdir, pdf documentation, ${DOCDIR}
psdir, ps documentation, ${DOCDIR}
'''.split('\n') if x]
def detect(conf):
def get_param(varname, default):
return getattr(Options.options, varname, '') or default
env = conf.env
env['EXEC_PREFIX'] = get_param('EXEC_PREFIX', env['PREFIX'])
env['PACKAGE'] = Utils.g_module.APPNAME
complete = False
iter = 0
while not complete and iter < len(_options) + 1:
iter += 1
complete = True
for name, help, default in _options:
name = name.upper()
if not env[name]:
try:
env[name] = Utils.subst_vars(get_param(name, default), env)
except TypeError:
complete = False
if not complete:
lst = [name for name, _, _ in _options if not env[name.upper()]]
raise Utils.WafError('Variable substitution failure %r' % lst)
def set_options(opt):
inst_dir = opt.add_option_group('Installation directories',
'By default, "waf install" will put the files in\
"/usr/local/bin", "/usr/local/lib" etc. An installation prefix other\
than "/usr/local" can be given using "--prefix", for example "--prefix=$HOME"')
for k in ('--prefix', '--destdir'):
option = opt.parser.get_option(k)
if option:
opt.parser.remove_option(k)
inst_dir.add_option(option)
inst_dir.add_option('--exec-prefix',
help = 'installation prefix [Default: ${PREFIX}]',
default = '',
dest = 'EXEC_PREFIX')
dirs_options = opt.add_option_group('Pre-defined installation directories', '')
for name, help, default in _options:
option_name = '--' + name
str_default = default
str_help = '%s [Default: %s]' % (help, str_default)
dirs_options.add_option(option_name, help=str_help, default='', dest=name.upper())
#!/usr/bin/env python
# encoding: utf-8
# Ali Sabil, 2007
import TaskGen
TaskGen.declare_chain(
name = 'gob2',
rule = '${GOB2} -o ${TGT[0].bld_dir(env)} ${GOB2FLAGS} ${SRC}',
ext_in = '.gob',
ext_out = '.c'
)
def detect(conf):
gob2 = conf.find_program('gob2', var='GOB2', mandatory=True)
conf.env['GOB2'] = gob2
conf.env['GOB2FLAGS'] = ''
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2006 (ita)
# Ralf Habacker, 2006 (rh)
# Yinon Ehrlich, 2009
import os, sys
import Configure, Options, Utils
import ccroot, ar
from Configure import conftest
@conftest
def find_gxx(conf):
cxx = conf.find_program(['g++', 'c++'], var='CXX', mandatory=True)
cxx = conf.cmd_to_list(cxx)
ccroot.get_cc_version(conf, cxx, gcc=True)
conf.env.CXX_NAME = 'gcc'
conf.env.CXX = cxx
@conftest
def gxx_common_flags(conf):
v = conf.env
# CPPFLAGS CXXDEFINES _CXXINCFLAGS _CXXDEFFLAGS
v['CXXFLAGS_DEBUG'] = ['-g']
v['CXXFLAGS_RELEASE'] = ['-O2']
v['CXX_SRC_F'] = ''
v['CXX_TGT_F'] = ['-c', '-o', ''] # shell hack for -MD
v['CPPPATH_ST'] = '-I%s' # template for adding include paths
# linker
if not v['LINK_CXX']: v['LINK_CXX'] = v['CXX']
v['CXXLNK_SRC_F'] = ''
v['CXXLNK_TGT_F'] = ['-o', ''] # shell hack for -MD
v['LIB_ST'] = '-l%s' # template for adding libs
v['LIBPATH_ST'] = '-L%s' # template for adding libpaths
v['STATICLIB_ST'] = '-l%s'
v['STATICLIBPATH_ST'] = '-L%s'
v['RPATH_ST'] = '-Wl,-rpath,%s'
v['CXXDEFINES_ST'] = '-D%s'
v['SONAME_ST'] = '-Wl,-h,%s'
v['SHLIB_MARKER'] = '-Wl,-Bdynamic'
v['STATICLIB_MARKER'] = '-Wl,-Bstatic'
v['FULLSTATIC_MARKER'] = '-static'
# program
v['program_PATTERN'] = '%s'
# shared library
v['shlib_CXXFLAGS'] = ['-fPIC', '-DPIC'] # avoid using -DPIC, -fPIC aleady defines the __PIC__ macro
v['shlib_LINKFLAGS'] = ['-shared']
v['shlib_PATTERN'] = 'lib%s.so'
# static lib
v['staticlib_LINKFLAGS'] = ['-Wl,-Bstatic']
v['staticlib_PATTERN'] = 'lib%s.a'
# osx stuff
v['LINKFLAGS_MACBUNDLE'] = ['-bundle', '-undefined', 'dynamic_lookup']
v['CCFLAGS_MACBUNDLE'] = ['-fPIC']
v['macbundle_PATTERN'] = '%s.bundle'
@conftest
def gxx_modifier_win32(conf):
v = conf.env
v['program_PATTERN'] = '%s.exe'
v['shlib_PATTERN'] = '%s.dll'
v['implib_PATTERN'] = 'lib%s.dll.a'
v['IMPLIB_ST'] = '-Wl,--out-implib,%s'
dest_arch = v['DEST_CPU']
if dest_arch == 'x86':
# On 32-bit x86, gcc emits a message telling the -fPIC option is ignored on this arch, so we remove that flag.
v['shlib_CXXFLAGS'] = ['-DPIC'] # TODO this is a wrong define, we don't use -fPIC!
v.append_value('shlib_CXXFLAGS', '-DDLL_EXPORT') # TODO adding nonstandard defines like this DLL_EXPORT is not a good idea
# Auto-import is enabled by default even without this option,
# but enabling it explicitly has the nice effect of suppressing the rather boring, debug-level messages
# that the linker emits otherwise.
v.append_value('LINKFLAGS', '-Wl,--enable-auto-import')
@conftest
def gxx_modifier_cygwin(conf):
gxx_modifier_win32(conf)
v = conf.env
v['shlib_PATTERN'] = 'cyg%s.dll'
v.append_value('shlib_LINKFLAGS', '-Wl,--enable-auto-image-base')
@conftest
def gxx_modifier_darwin(conf):
v = conf.env
v['shlib_CXXFLAGS'] = ['-fPIC', '-compatibility_version', '1', '-current_version', '1']
v['shlib_LINKFLAGS'] = ['-dynamiclib']
v['shlib_PATTERN'] = 'lib%s.dylib'
v['staticlib_LINKFLAGS'] = []
v['SHLIB_MARKER'] = ''
v['STATICLIB_MARKER'] = ''
v['SONAME_ST'] = ''
@conftest
def gxx_modifier_aix(conf):
v = conf.env
v['program_LINKFLAGS'] = ['-Wl,-brtl']
v['shlib_LINKFLAGS'] = ['-shared', '-Wl,-brtl,-bexpfull']
v['SHLIB_MARKER'] = ''
@conftest
def gxx_modifier_platform(conf):
# * set configurations specific for a platform.
# * the destination platform is detected automatically by looking at the macros the compiler predefines,
# and if it's not recognised, it fallbacks to sys.platform.
dest_os = conf.env['DEST_OS'] or Utils.unversioned_sys_platform()
gxx_modifier_func = globals().get('gxx_modifier_' + dest_os)
if gxx_modifier_func:
gxx_modifier_func(conf)
def detect(conf):
conf.find_gxx()
conf.find_cpp()
conf.find_ar()
conf.gxx_common_flags()
conf.gxx_modifier_platform()
conf.cxx_load_tools()
conf.cxx_add_flags()
#!/usr/bin/env python
# encoding: utf-8
# Stian Selnes, 2008
# Thomas Nagy 2009
import os, sys
import Configure, Options, Utils
import ccroot, ar, gcc
from Configure import conftest
@conftest
def find_icc(conf):
if sys.platform == 'cygwin':
conf.fatal('The Intel compiler does not work on Cygwin')
v = conf.env
cc = None
if v['CC']: cc = v['CC']
elif 'CC' in conf.environ: cc = conf.environ['CC']
if not cc: cc = conf.find_program('icc', var='CC')
if not cc: cc = conf.find_program('ICL', var='CC')
if not cc: conf.fatal('Intel C Compiler (icc) was not found')
cc = conf.cmd_to_list(cc)
ccroot.get_cc_version(conf, cc, icc=True)
v['CC'] = cc
v['CC_NAME'] = 'icc'
detect = '''
find_icc
find_ar
gcc_common_flags
gcc_modifier_platform
cc_load_tools
cc_add_flags
link_add_flags
'''
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy 2009
import os, sys
import Configure, Options, Utils
import ccroot, ar, gxx
from Configure import conftest
@conftest
def find_icpc(conf):
if sys.platform == 'cygwin':
conf.fatal('The Intel compiler does not work on Cygwin')
v = conf.env
cxx = None
if v['CXX']: cxx = v['CXX']
elif 'CXX' in conf.environ: cxx = conf.environ['CXX']
if not cxx: cxx = conf.find_program('icpc', var='CXX')
if not cxx: conf.fatal('Intel C++ Compiler (icpc) was not found')
cxx = conf.cmd_to_list(cxx)
ccroot.get_cc_version(conf, cxx, icc=True)
v['CXX'] = cxx
v['CXX_NAME'] = 'icc'
detect = '''
find_icpc
find_ar
gxx_common_flags
gxx_modifier_platform
cxx_load_tools
cxx_add_flags
'''
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2006 (ita)
"intltool support"
import os, re
import Configure, TaskGen, Task, Utils, Runner, Options, Build, config_c
from TaskGen import feature, before, taskgen
from Logs import error
"""
Usage:
bld(features='intltool_in', source='a.po b.po', podir='po', cache='.intlcache', flags='')
"""
class intltool_in_taskgen(TaskGen.task_gen):
"""deprecated"""
def __init__(self, *k, **kw):
TaskGen.task_gen.__init__(self, *k, **kw)
@before('apply_core')
@feature('intltool_in')
def iapply_intltool_in_f(self):
try: self.meths.remove('apply_core')
except ValueError: pass
for i in self.to_list(self.source):
node = self.path.find_resource(i)
podir = getattr(self, 'podir', 'po')
podirnode = self.path.find_dir(podir)
if not podirnode:
error("could not find the podir %r" % podir)
continue
cache = getattr(self, 'intlcache', '.intlcache')
self.env['INTLCACHE'] = os.path.join(self.path.bldpath(self.env), podir, cache)
self.env['INTLPODIR'] = podirnode.srcpath(self.env)
self.env['INTLFLAGS'] = getattr(self, 'flags', ['-q', '-u', '-c'])
task = self.create_task('intltool', node, node.change_ext(''))
task.install_path = self.install_path
class intltool_po_taskgen(TaskGen.task_gen):
"""deprecated"""
def __init__(self, *k, **kw):
TaskGen.task_gen.__init__(self, *k, **kw)
@feature('intltool_po')
def apply_intltool_po(self):
try: self.meths.remove('apply_core')
except ValueError: pass
self.default_install_path = '${LOCALEDIR}'
appname = getattr(self, 'appname', 'set_your_app_name')
podir = getattr(self, 'podir', '')
def install_translation(task):
out = task.outputs[0]
filename = out.name
(langname, ext) = os.path.splitext(filename)
inst_file = langname + os.sep + 'LC_MESSAGES' + os.sep + appname + '.mo'
self.bld.install_as(os.path.join(self.install_path, inst_file), out, self.env, self.chmod)
linguas = self.path.find_resource(os.path.join(podir, 'LINGUAS'))
if linguas:
# scan LINGUAS file for locales to process
file = open(linguas.abspath())
langs = []
for line in file.readlines():
# ignore lines containing comments
if not line.startswith('#'):
langs += line.split()
file.close()
re_linguas = re.compile('[-a-zA-Z_@.]+')
for lang in langs:
# Make sure that we only process lines which contain locales
if re_linguas.match(lang):
node = self.path.find_resource(os.path.join(podir, re_linguas.match(lang).group() + '.po'))
task = self.create_task('po')
task.set_inputs(node)
task.set_outputs(node.change_ext('.mo'))
if self.bld.is_install: task.install = install_translation
else:
Utils.pprint('RED', "Error no LINGUAS file found in po directory")
Task.simple_task_type('po', '${POCOM} -o ${TGT} ${SRC}', color='BLUE', shell=False)
Task.simple_task_type('intltool',
'${INTLTOOL} ${INTLFLAGS} ${INTLCACHE} ${INTLPODIR} ${SRC} ${TGT}',
color='BLUE', after="cc_link cxx_link", shell=False)
def detect(conf):
pocom = conf.find_program('msgfmt')
if not pocom:
# if msgfmt should not be mandatory, catch the thrown exception in your wscript
conf.fatal('The program msgfmt (gettext) is mandatory!')
conf.env['POCOM'] = pocom
# NOTE: it is possible to set INTLTOOL in the environment, but it must not have spaces in it
intltool = conf.find_program('intltool-merge', var='INTLTOOL')
if not intltool:
# if intltool-merge should not be mandatory, catch the thrown exception in your wscript
if Options.platform == 'win32':
perl = conf.find_program('perl', var='PERL')
if not perl:
conf.fatal('The program perl (required by intltool) could not be found')
intltooldir = Configure.find_file('intltool-merge', os.environ['PATH'].split(os.pathsep))
if not intltooldir:
conf.fatal('The program intltool-merge (intltool, gettext-devel) is mandatory!')
conf.env['INTLTOOL'] = Utils.to_list(conf.env['PERL']) + [intltooldir + os.sep + 'intltool-merge']
conf.check_message('intltool', '', True, ' '.join(conf.env['INTLTOOL']))
else:
conf.fatal('The program intltool-merge (intltool, gettext-devel) is mandatory!')
def getstr(varname):
return getattr(Options.options, varname, '')
prefix = conf.env['PREFIX']
datadir = getstr('datadir')
if not datadir: datadir = os.path.join(prefix,'share')
conf.define('LOCALEDIR', os.path.join(datadir, 'locale'))
conf.define('DATADIR', datadir)
if conf.env['CC'] or conf.env['CXX']:
# Define to 1 if <locale.h> is present
conf.check(header_name='locale.h')
def set_options(opt):
opt.add_option('--want-rpath', type='int', default=1, dest='want_rpath', help='set rpath to 1 or 0 [Default 1]')
opt.add_option('--datadir', type='string', default='', dest='datadir', help='read-only application data')
#!/usr/bin/env python
# encoding: utf-8
# Matthias Jahn, 2008, jahn matthias ath freenet punto de
# Thomas Nagy, 2008 (ita)
import sys, re, os, optparse
import TaskGen, Task, Utils, preproc
from Logs import error, debug, warn
from TaskGen import taskgen, after, before, feature
REVISION="0.1.3"
"""
if you want to use the code here, you must use something like this:
obj = obj.create(...)
obj.features.append("libtool")
obj.vnum = "1.2.3" # optional, but versioned libraries are common
"""
# fake libtool files
fakelibtool_vardeps = ['CXX', 'PREFIX']
def fakelibtool_build(task):
# Writes a .la file, used by libtool
env = task.env
dest = open(task.outputs[0].abspath(env), 'w')
sname = task.inputs[0].name
fu = dest.write
fu("# Generated by ltmain.sh - GNU libtool 1.5.18 - (pwn3d by BKsys II code name WAF)\n")
if env['vnum']:
nums = env['vnum'].split('.')
libname = task.inputs[0].name
name3 = libname+'.'+env['vnum']
name2 = libname+'.'+nums[0]
name1 = libname
fu("dlname='%s'\n" % name2)
strn = " ".join([name3, name2, name1])
fu("library_names='%s'\n" % (strn) )
else:
fu("dlname='%s'\n" % sname)
fu("library_names='%s %s %s'\n" % (sname, sname, sname) )
fu("old_library=''\n")
vars = ' '.join(env['libtoolvars']+env['LINKFLAGS'])
fu("dependency_libs='%s'\n" % vars)
fu("current=0\n")
fu("age=0\nrevision=0\ninstalled=yes\nshouldnotlink=no\n")
fu("dlopen=''\ndlpreopen=''\n")
fu("libdir='%s/lib'\n" % env['PREFIX'])
dest.close()
return 0
def read_la_file(path):
sp = re.compile(r'^([^=]+)=\'(.*)\'$')
dc={}
file = open(path, "r")
for line in file.readlines():
try:
#print sp.split(line.strip())
_, left, right, _ = sp.split(line.strip())
dc[left]=right
except ValueError:
pass
file.close()
return dc
@feature("libtool")
@after('apply_link')
def apply_link_libtool(self):
if self.type != 'program':
linktask = self.link_task
self.latask = self.create_task('fakelibtool', linktask.outputs, linktask.outputs[0].change_ext('.la'))
if self.bld.is_install:
self.bld.install_files('${PREFIX}/lib', linktask.outputs[0], self.env)
@feature("libtool")
@before('apply_core')
def apply_libtool(self):
self.env['vnum']=self.vnum
paths=[]
libs=[]
libtool_files=[]
libtool_vars=[]
for l in self.env['LINKFLAGS']:
if l[:2]=='-L':
paths.append(l[2:])
elif l[:2]=='-l':
libs.append(l[2:])
for l in libs:
for p in paths:
dict = read_la_file(p+'/lib'+l+'.la')
linkflags2 = dict.get('dependency_libs', '')
for v in linkflags2.split():
if v.endswith('.la'):
libtool_files.append(v)
libtool_vars.append(v)
continue
self.env.append_unique('LINKFLAGS', v)
break
self.env['libtoolvars']=libtool_vars
while libtool_files:
file = libtool_files.pop()
dict = read_la_file(file)
for v in dict['dependency_libs'].split():
if v[-3:] == '.la':
libtool_files.append(v)
continue
self.env.append_unique('LINKFLAGS', v)
Task.task_type_from_func('fakelibtool', vars=fakelibtool_vardeps, func=fakelibtool_build, color='BLUE', after="cc_link cxx_link static_link")
class libtool_la_file:
def __init__ (self, la_filename):
self.__la_filename = la_filename
#remove path and .la suffix
self.linkname = str(os.path.split(la_filename)[-1])[:-3]
if self.linkname.startswith("lib"):
self.linkname = self.linkname[3:]
# The name that we can dlopen(3).
self.dlname = None
# Names of this library
self.library_names = None
# The name of the static archive.
self.old_library = None
# Libraries that this one depends upon.
self.dependency_libs = None
# Version information for libIlmImf.
self.current = None
self.age = None
self.revision = None
# Is this an already installed library?
self.installed = None
# Should we warn about portability when linking against -modules?
self.shouldnotlink = None
# Files to dlopen/dlpreopen
self.dlopen = None
self.dlpreopen = None
# Directory that this library needs to be installed in:
self.libdir = '/usr/lib'
if not self.__parse():
raise "file %s not found!!" %(la_filename)
def __parse(self):
"Retrieve the variables from a file"
if not os.path.isfile(self.__la_filename): return 0
la_file=open(self.__la_filename, 'r')
for line in la_file:
ln = line.strip()
if not ln: continue
if ln[0]=='#': continue
(key, value) = str(ln).split('=', 1)
key = key.strip()
value = value.strip()
if value == "no": value = False
elif value == "yes": value = True
else:
try: value = int(value)
except ValueError: value = value.strip("'")
setattr(self, key, value)
la_file.close()
return 1
def get_libs(self):
"""return linkflags for this lib"""
libs = []
if self.dependency_libs:
libs = str(self.dependency_libs).strip().split()
if libs == None:
libs = []
# add la lib and libdir
libs.insert(0, "-l%s" % self.linkname.strip())
libs.insert(0, "-L%s" % self.libdir.strip())
return libs
def __str__(self):
return '''\
dlname = "%(dlname)s"
library_names = "%(library_names)s"
old_library = "%(old_library)s"
dependency_libs = "%(dependency_libs)s"
version = %(current)s.%(age)s.%(revision)s
installed = "%(installed)s"
shouldnotlink = "%(shouldnotlink)s"
dlopen = "%(dlopen)s"
dlpreopen = "%(dlpreopen)s"
libdir = "%(libdir)s"''' % self.__dict__
class libtool_config:
def __init__ (self, la_filename):
self.__libtool_la_file = libtool_la_file(la_filename)
tmp = self.__libtool_la_file
self.__version = [int(tmp.current), int(tmp.age), int(tmp.revision)]
self.__sub_la_files = []
self.__sub_la_files.append(la_filename)
self.__libs = None
def __cmp__(self, other):
"""make it compareable with X.Y.Z versions (Y and Z are optional)"""
if not other:
return 1
othervers = [int(s) for s in str(other).split(".")]
selfvers = self.__version
return cmp(selfvers, othervers)
def __str__(self):
return "\n".join([
str(self.__libtool_la_file),
' '.join(self.__libtool_la_file.get_libs()),
'* New getlibs:',
' '.join(self.get_libs())
])
def __get_la_libs(self, la_filename):
return libtool_la_file(la_filename).get_libs()
def get_libs(self):
"""return the complete uniqe linkflags that do not
contain .la files anymore"""
libs_list = list(self.__libtool_la_file.get_libs())
libs_map = {}
while len(libs_list) > 0:
entry = libs_list.pop(0)
if entry:
if str(entry).endswith(".la"):
## prevents duplicate .la checks
if entry not in self.__sub_la_files:
self.__sub_la_files.append(entry)
libs_list.extend(self.__get_la_libs(entry))
else:
libs_map[entry]=1
self.__libs = libs_map.keys()
return self.__libs
def get_libs_only_L(self):
if not self.__libs: self.get_libs()
libs = self.__libs
libs = [s for s in libs if str(s).startswith('-L')]
return libs
def get_libs_only_l(self):
if not self.__libs: self.get_libs()
libs = self.__libs
libs = [s for s in libs if str(s).startswith('-l')]
return libs
def get_libs_only_other(self):
if not self.__libs: self.get_libs()
libs = self.__libs
libs = [s for s in libs if not(str(s).startswith('-L')or str(s).startswith('-l'))]
return libs
def useCmdLine():
"""parse cmdline args and control build"""
usage = '''Usage: %prog [options] PathToFile.la
example: %prog --atleast-version=2.0.0 /usr/lib/libIlmImf.la
nor: %prog --libs /usr/lib/libamarok.la'''
parser = optparse.OptionParser(usage)
a = parser.add_option
a("--version", dest = "versionNumber",
action = "store_true", default = False,
help = "output version of libtool-config"
)
a("--debug", dest = "debug",
action = "store_true", default = False,
help = "enable debug"
)
a("--libs", dest = "libs",
action = "store_true", default = False,
help = "output all linker flags"
)
a("--libs-only-l", dest = "libs_only_l",
action = "store_true", default = False,
help = "output -l flags"
)
a("--libs-only-L", dest = "libs_only_L",
action = "store_true", default = False,
help = "output -L flags"
)
a("--libs-only-other", dest = "libs_only_other",
action = "store_true", default = False,
help = "output other libs (e.g. -pthread)"
)
a("--atleast-version", dest = "atleast_version",
default=None,
help = "return 0 if the module is at least version ATLEAST_VERSION"
)
a("--exact-version", dest = "exact_version",
default=None,
help = "return 0 if the module is exactly version EXACT_VERSION"
)
a("--max-version", dest = "max_version",
default=None,
help = "return 0 if the module is at no newer than version MAX_VERSION"
)
(options, args) = parser.parse_args()
if len(args) != 1 and not options.versionNumber:
parser.error("incorrect number of arguments")
if options.versionNumber:
print("libtool-config version %s" % REVISION)
return 0
ltf = libtool_config(args[0])
if options.debug:
print(ltf)
if options.atleast_version:
if ltf >= options.atleast_version: return 0
sys.exit(1)
if options.exact_version:
if ltf == options.exact_version: return 0
sys.exit(1)
if options.max_version:
if ltf <= options.max_version: return 0
sys.exit(1)
def p(x):
print(" ".join(x))
if options.libs: p(ltf.get_libs())
elif options.libs_only_l: p(ltf.get_libs_only_l())
elif options.libs_only_L: p(ltf.get_libs_only_L())
elif options.libs_only_other: p(ltf.get_libs_only_other())
return 0
if __name__ == '__main__':
useCmdLine()
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2006 (ita)
"""
Custom objects:
- execute a function everytime
- copy a file somewhere else
"""
import shutil, re, os
import TaskGen, Node, Task, Utils, Build, Constants
from TaskGen import feature, taskgen, after, before
from Logs import debug
def copy_func(tsk):
"Make a file copy. This might be used to make other kinds of file processing (even calling a compiler is possible)"
env = tsk.env
infile = tsk.inputs[0].abspath(env)
outfile = tsk.outputs[0].abspath(env)
try:
shutil.copy2(infile, outfile)
except (OSError, IOError):
return 1
else:
if tsk.chmod: os.chmod(outfile, tsk.chmod)
return 0
def action_process_file_func(tsk):
"Ask the function attached to the task to process it"
if not tsk.fun: raise Utils.WafError('task must have a function attached to it for copy_func to work!')
return tsk.fun(tsk)
class cmd_taskgen(TaskGen.task_gen):
def __init__(self, *k, **kw):
TaskGen.task_gen.__init__(self, *k, **kw)
@feature('cmd')
def apply_cmd(self):
"call a command everytime"
if not self.fun: raise Utils.WafError('cmdobj needs a function!')
tsk = Task.TaskBase()
tsk.fun = self.fun
tsk.env = self.env
self.tasks.append(tsk)
tsk.install_path = self.install_path
class copy_taskgen(TaskGen.task_gen):
"By default, make a file copy, if fun is provided, fun will make the copy (or call a compiler, etc)"
def __init__(self, *k, **kw):
TaskGen.task_gen.__init__(self, *k, **kw)
@feature('copy')
@before('apply_core')
def apply_copy(self):
Utils.def_attrs(self, fun=copy_func)
self.default_install_path = 0
lst = self.to_list(self.source)
self.meths.remove('apply_core')
for filename in lst:
node = self.path.find_resource(filename)
if not node: raise Utils.WafError('cannot find input file %s for processing' % filename)
target = self.target
if not target or len(lst)>1: target = node.name
# TODO the file path may be incorrect
newnode = self.path.find_or_declare(target)
tsk = self.create_task('copy', node, newnode)
tsk.fun = self.fun
tsk.chmod = self.chmod
tsk.install_path = self.install_path
if not tsk.env:
tsk.debug()
raise Utils.WafError('task without an environment')
def subst_func(tsk):
"Substitutes variables in a .in file"
m4_re = re.compile('@(\w+)@', re.M)
env = tsk.env
infile = tsk.inputs[0].abspath(env)
outfile = tsk.outputs[0].abspath(env)
code = Utils.readf(infile)
# replace all % by %% to prevent errors by % signs in the input file while string formatting
code = code.replace('%', '%%')
s = m4_re.sub(r'%(\1)s', code)
di = tsk.dict or {}
if not di:
names = m4_re.findall(code)
for i in names:
di[i] = env.get_flat(i) or env.get_flat(i.upper())
file = open(outfile, 'w')
file.write(s % di)
file.close()
if tsk.chmod: os.chmod(outfile, tsk.chmod)
class subst_taskgen(TaskGen.task_gen):
def __init__(self, *k, **kw):
TaskGen.task_gen.__init__(self, *k, **kw)
@feature('subst')
@before('apply_core')
def apply_subst(self):
Utils.def_attrs(self, fun=subst_func)
self.default_install_path = 0
lst = self.to_list(self.source)
self.meths.remove('apply_core')
self.dict = getattr(self, 'dict', {})
for filename in lst:
node = self.path.find_resource(filename)
if not node: raise Utils.WafError('cannot find input file %s for processing' % filename)
if self.target:
newnode = self.path.find_or_declare(self.target)
else:
newnode = node.change_ext('')
try:
self.dict = self.dict.get_merged_dict()
except AttributeError:
pass
if self.dict and not self.env['DICT_HASH']:
self.env = self.env.copy()
keys = list(self.dict.keys())
keys.sort()
lst = [self.dict[x] for x in keys]
self.env['DICT_HASH'] = str(Utils.h_list(lst))
tsk = self.create_task('copy', node, newnode)
tsk.fun = self.fun
tsk.dict = self.dict
tsk.dep_vars = ['DICT_HASH']
tsk.install_path = self.install_path
tsk.chmod = self.chmod
if not tsk.env:
tsk.debug()
raise Utils.WafError('task without an environment')
####################
## command-output ####
####################
class cmd_arg(object):
"""command-output arguments for representing files or folders"""
def __init__(self, name, template='%s'):
self.name = name
self.template = template
self.node = None
class input_file(cmd_arg):
def find_node(self, base_path):
assert isinstance(base_path, Node.Node)
self.node = base_path.find_resource(self.name)
if self.node is None:
raise Utils.WafError("Input file %s not found in " % (self.name, base_path))
def get_path(self, env, absolute):
if absolute:
return self.template % self.node.abspath(env)
else:
return self.template % self.node.srcpath(env)
class output_file(cmd_arg):
def find_node(self, base_path):
assert isinstance(base_path, Node.Node)
self.node = base_path.find_or_declare(self.name)
if self.node is None:
raise Utils.WafError("Output file %s not found in " % (self.name, base_path))
def get_path(self, env, absolute):
if absolute:
return self.template % self.node.abspath(env)
else:
return self.template % self.node.bldpath(env)
class cmd_dir_arg(cmd_arg):
def find_node(self, base_path):
assert isinstance(base_path, Node.Node)
self.node = base_path.find_dir(self.name)
if self.node is None:
raise Utils.WafError("Directory %s not found in " % (self.name, base_path))
class input_dir(cmd_dir_arg):
def get_path(self, dummy_env, dummy_absolute):
return self.template % self.node.abspath()
class output_dir(cmd_dir_arg):
def get_path(self, env, dummy_absolute):
return self.template % self.node.abspath(env)
class command_output(Task.Task):
color = "BLUE"
def __init__(self, env, command, command_node, command_args, stdin, stdout, cwd, os_env, stderr):
Task.Task.__init__(self, env, normal=1)
assert isinstance(command, (str, Node.Node))
self.command = command
self.command_args = command_args
self.stdin = stdin
self.stdout = stdout
self.cwd = cwd
self.os_env = os_env
self.stderr = stderr
if command_node is not None: self.dep_nodes = [command_node]
self.dep_vars = [] # additional environment variables to look
def run(self):
task = self
#assert len(task.inputs) > 0
def input_path(node, template):
if task.cwd is None:
return template % node.bldpath(task.env)
else:
return template % node.abspath()
def output_path(node, template):
fun = node.abspath
if task.cwd is None: fun = node.bldpath
return template % fun(task.env)
if isinstance(task.command, Node.Node):
argv = [input_path(task.command, '%s')]
else:
argv = [task.command]
for arg in task.command_args:
if isinstance(arg, str):
argv.append(arg)
else:
assert isinstance(arg, cmd_arg)
argv.append(arg.get_path(task.env, (task.cwd is not None)))
if task.stdin:
stdin = open(input_path(task.stdin, '%s'))
else:
stdin = None
if task.stdout:
stdout = open(output_path(task.stdout, '%s'), "w")
else:
stdout = None
if task.stderr:
stderr = open(output_path(task.stderr, '%s'), "w")
else:
stderr = None
if task.cwd is None:
cwd = ('None (actually %r)' % os.getcwd())
else:
cwd = repr(task.cwd)
debug("command-output: cwd=%s, stdin=%r, stdout=%r, argv=%r" %
(cwd, stdin, stdout, argv))
if task.os_env is None:
os_env = os.environ
else:
os_env = task.os_env
command = Utils.pproc.Popen(argv, stdin=stdin, stdout=stdout, stderr=stderr, cwd=task.cwd, env=os_env)
return command.wait()
class cmd_output_taskgen(TaskGen.task_gen):
def __init__(self, *k, **kw):
TaskGen.task_gen.__init__(self, *k, **kw)
@feature('command-output')
def init_cmd_output(self):
Utils.def_attrs(self,
stdin = None,
stdout = None,
stderr = None,
# the command to execute
command = None,
# whether it is an external command; otherwise it is assumed
# to be an executable binary or script that lives in the
# source or build tree.
command_is_external = False,
# extra parameters (argv) to pass to the command (excluding
# the command itself)
argv = [],
# dependencies to other objects -> this is probably not what you want (ita)
# values must be 'task_gen' instances (not names!)
dependencies = [],
# dependencies on env variable contents
dep_vars = [],
# input files that are implicit, i.e. they are not
# stdin, nor are they mentioned explicitly in argv
hidden_inputs = [],
# output files that are implicit, i.e. they are not
# stdout, nor are they mentioned explicitly in argv
hidden_outputs = [],
# change the subprocess to this cwd (must use obj.input_dir() or output_dir() here)
cwd = None,
# OS environment variables to pass to the subprocess
# if None, use the default environment variables unchanged
os_env = None)
@feature('command-output')
@after('init_cmd_output')
def apply_cmd_output(self):
if self.command is None:
raise Utils.WafError("command-output missing command")
if self.command_is_external:
cmd = self.command
cmd_node = None
else:
cmd_node = self.path.find_resource(self.command)
assert cmd_node is not None, ('''Could not find command '%s' in source tree.
Hint: if this is an external command,
use command_is_external=True''') % (self.command,)
cmd = cmd_node
if self.cwd is None:
cwd = None
else:
assert isinstance(cwd, CmdDirArg)
self.cwd.find_node(self.path)
args = []
inputs = []
outputs = []
for arg in self.argv:
if isinstance(arg, cmd_arg):
arg.find_node(self.path)
if isinstance(arg, input_file):
inputs.append(arg.node)
if isinstance(arg, output_file):
outputs.append(arg.node)
if self.stdout is None:
stdout = None
else:
assert isinstance(self.stdout, str)
stdout = self.path.find_or_declare(self.stdout)
if stdout is None:
raise Utils.WafError("File %s not found" % (self.stdout,))
outputs.append(stdout)
if self.stderr is None:
stderr = None
else:
assert isinstance(self.stderr, str)
stderr = self.path.find_or_declare(self.stderr)
if stderr is None:
raise Utils.WafError("File %s not found" % (self.stderr,))
outputs.append(stderr)
if self.stdin is None:
stdin = None
else:
assert isinstance(self.stdin, str)
stdin = self.path.find_resource(self.stdin)
if stdin is None:
raise Utils.WafError("File %s not found" % (self.stdin,))
inputs.append(stdin)
for hidden_input in self.to_list(self.hidden_inputs):
node = self.path.find_resource(hidden_input)
if node is None:
raise Utils.WafError("File %s not found in dir %s" % (hidden_input, self.path))
inputs.append(node)
for hidden_output in self.to_list(self.hidden_outputs):
node = self.path.find_or_declare(hidden_output)
if node is None:
raise Utils.WafError("File %s not found in dir %s" % (hidden_output, self.path))
outputs.append(node)
if not (inputs or getattr(self, 'no_inputs', None)):
raise Utils.WafError('command-output objects must have at least one input file or give self.no_inputs')
if not (outputs or getattr(self, 'no_outputs', None)):
raise Utils.WafError('command-output objects must have at least one output file or give self.no_outputs')
task = command_output(self.env, cmd, cmd_node, self.argv, stdin, stdout, cwd, self.os_env, stderr)
Utils.copy_attrs(self, task, 'before after ext_in ext_out', only_if_set=True)
self.tasks.append(task)
task.inputs = inputs
task.outputs = outputs
task.dep_vars = self.to_list(self.dep_vars)
for dep in self.dependencies:
assert dep is not self
dep.post()
for dep_task in dep.tasks:
task.set_run_after(dep_task)
if not task.inputs:
# the case for svnversion, always run, and update the output nodes
task.runnable_status = type(Task.TaskBase.run)(runnable_status, task, task.__class__) # always run
task.post_run = type(Task.TaskBase.run)(post_run, task, task.__class__)
# TODO the case with no outputs?
def post_run(self):
for x in self.outputs:
h = Utils.h_file(x.abspath(self.env))
self.generator.bld.node_sigs[self.env.variant()][x.id] = h
def runnable_status(self):
return Constants.RUN_ME
Task.task_type_from_func('copy', vars=[], func=action_process_file_func)
TaskGen.task_gen.classes['command-output'] = cmd_output_taskgen
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2008
"""
Nasm processing
"""
import os
import TaskGen, Task, Utils
from TaskGen import taskgen, before, extension
nasm_str = '${NASM} ${NASM_FLAGS} ${NASM_INCLUDES} ${SRC} -o ${TGT}'
EXT_NASM = ['.s', '.S', '.asm', '.ASM', '.spp', '.SPP']
@before('apply_link')
def apply_nasm_vars(self):
# flags
if hasattr(self, 'nasm_flags'):
for flag in self.to_list(self.nasm_flags):
self.env.append_value('NASM_FLAGS', flag)
# includes - well, if we suppose it works with c processing
if hasattr(self, 'includes'):
for inc in self.to_list(self.includes):
node = self.path.find_dir(inc)
if not node:
raise Utils.WafError('cannot find the dir' + inc)
self.env.append_value('NASM_INCLUDES', '-I%s' % node.srcpath(self.env))
self.env.append_value('NASM_INCLUDES', '-I%s' % node.bldpath(self.env))
@extension(EXT_NASM)
def nasm_file(self, node):
try: obj_ext = self.obj_ext
except AttributeError: obj_ext = '_%d.o' % self.idx
task = self.create_task('nasm', node, node.change_ext(obj_ext))
self.compiled_tasks.append(task)
self.meths.append('apply_nasm_vars')
# create our action here
Task.simple_task_type('nasm', nasm_str, color='BLUE', ext_out='.o', shell=False)
def detect(conf):
nasm = conf.find_program(['nasm', 'yasm'], var='NASM', mandatory=True)
import os
import TaskGen, Utils, Utils, Runner, Options, Build
from TaskGen import extension, taskgen, before, after, feature
from Configure import conf, conftest
@taskgen
@before('apply_incpaths', 'apply_lib_vars', 'apply_type_vars')
@feature('node_addon')
@before('apply_bundle')
def init_node_addon(self):
self.default_install_path = self.env['NODE_PATH']
self.uselib = self.to_list(getattr(self, 'uselib', ''))
if not 'NODE' in self.uselib: self.uselib.append('NODE')
self.env['MACBUNDLE'] = True
@taskgen
@before('apply_link', 'apply_lib_vars', 'apply_type_vars')
@after('apply_bundle')
@feature('node_addon')
def node_addon_shlib_ext(self):
self.env['shlib_PATTERN'] = "%s.node"
def detect(conf):
join = os.path.join
conf.env['PREFIX_NODE'] = get_prefix()
prefix = conf.env['PREFIX_NODE']
lib = join(prefix, 'lib')
conf.env['LIBPATH_NODE'] = lib
conf.env['CPPPATH_NODE'] = join(prefix, 'include', 'node')
conf.env.append_value('CPPFLAGS_NODE', '-D_GNU_SOURCE')
conf.env.append_value('CPPFLAGS_NODE', '-DEV_MULTIPLICITY=0')
conf.env.append_value('CCFLAGS_NODE', '-D_LARGEFILE_SOURCE')
conf.env.append_value('CCFLAGS_NODE', '-D_FILE_OFFSET_BITS=64')
conf.env.append_value('CXXFLAGS_NODE', '-D_LARGEFILE_SOURCE')
conf.env.append_value('CXXFLAGS_NODE', '-D_FILE_OFFSET_BITS=64')
# with symbols
conf.env.append_value('CCFLAGS', ['-g'])
conf.env.append_value('CXXFLAGS', ['-g'])
# install path
conf.env['NODE_PATH'] = get_node_path()
# this changes the install path of cxx task_gen
conf.env['LIBDIR'] = conf.env['NODE_PATH']
found = os.path.exists(conf.env['NODE_PATH'])
conf.check_message('node path', '', found, conf.env['NODE_PATH'])
found = os.path.exists(join(prefix, 'bin', 'node'))
conf.check_message('node prefix', '', found, prefix)
## On Cygwin we need to link to the generated symbol definitions
if Options.platform.startswith('cygwin'): conf.env['LIB_NODE'] = 'node'
## On Mac OSX we need to use mac bundles
if Options.platform == 'darwin': conf.check_tool('osx')
def get_node_path():
join = os.path.join
nodePath = None
if not os.environ.has_key('NODE_PATH'):
if not os.environ.has_key('HOME'):
nodePath = join(get_prefix(), 'lib', 'node')
else:
nodePath = join(os.environ['HOME'], '.node_libraries')
else:
nodePath = os.environ['NODE_PATH']
return nodePath
def get_prefix():
prefix = None
if not os.environ.has_key('PREFIX_NODE'):
prefix = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', '..'))
else:
prefix = os.environ['PREFIX_NODE']
return prefix
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy 2008
"""MacOSX related tools
To compile an executable into a Mac application bundle (a .app), set its 'mac_app' attribute
obj.mac_app = True
To make a bundled shared library (a .bundle), set the 'mac_bundle' attribute:
obj.mac_bundle = True
"""
import os, shutil, sys, platform
import TaskGen, Task, Build, Options, Utils
from TaskGen import taskgen, feature, after, before
from Logs import error, debug
# plist template
app_info = '''
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
<plist version="0.9">
<dict>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleGetInfoString</key>
<string>Created by Waf</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>NOTE</key>
<string>THIS IS A GENERATED FILE, DO NOT MODIFY</string>
<key>CFBundleExecutable</key>
<string>%s</string>
</dict>
</plist>
'''
# see WAF issue 285
# and also http://trac.macports.org/ticket/17059
@feature('cc', 'cxx')
@before('apply_lib_vars')
def set_macosx_deployment_target(self):
if self.env['MACOSX_DEPLOYMENT_TARGET']:
os.environ['MACOSX_DEPLOYMENT_TARGET'] = self.env['MACOSX_DEPLOYMENT_TARGET']
elif 'MACOSX_DEPLOYMENT_TARGET' not in os.environ:
if sys.platform == 'darwin':
os.environ['MACOSX_DEPLOYMENT_TARGET'] = '.'.join(platform.mac_ver()[0].split('.')[:2])
@feature('cc', 'cxx')
@after('apply_lib_vars')
def apply_framework(self):
for x in self.to_list(self.env['FRAMEWORKPATH']):
frameworkpath_st = '-F%s'
self.env.append_unique('CXXFLAGS', frameworkpath_st % x)
self.env.append_unique('CCFLAGS', frameworkpath_st % x)
self.env.append_unique('LINKFLAGS', frameworkpath_st % x)
for x in self.to_list(self.env['FRAMEWORK']):
self.env.append_value('LINKFLAGS', ['-framework', x])
@taskgen
def create_bundle_dirs(self, name, out):
bld = self.bld
dir = out.parent.get_dir(name)
if not dir:
dir = out.__class__(name, out.parent, 1)
bld.rescan(dir)
contents = out.__class__('Contents', dir, 1)
bld.rescan(contents)
macos = out.__class__('MacOS', contents, 1)
bld.rescan(macos)
return dir
def bundle_name_for_output(out):
name = out.name
k = name.rfind('.')
if k >= 0:
name = name[:k] + '.app'
else:
name = name + '.app'
return name
@taskgen
@after('apply_link')
@feature('cprogram')
def create_task_macapp(self):
"""Use env['MACAPP'] to force *all* executables to be transformed into Mac applications
or use obj.mac_app = True to build specific targets as Mac apps"""
if self.env['MACAPP'] or getattr(self, 'mac_app', False):
apptask = self.create_task('macapp')
apptask.set_inputs(self.link_task.outputs)
out = self.link_task.outputs[0]
name = bundle_name_for_output(out)
dir = self.create_bundle_dirs(name, out)
n1 = dir.find_or_declare(['Contents', 'MacOS', out.name])
apptask.set_outputs([n1])
apptask.chmod = 0755
apptask.install_path = os.path.join(self.install_path, name, 'Contents', 'MacOS')
self.apptask = apptask
@after('apply_link')
@feature('cprogram')
def create_task_macplist(self):
"""Use env['MACAPP'] to force *all* executables to be transformed into Mac applications
or use obj.mac_app = True to build specific targets as Mac apps"""
if self.env['MACAPP'] or getattr(self, 'mac_app', False):
# check if the user specified a plist before using our template
if not getattr(self, 'mac_plist', False):
self.mac_plist = app_info
plisttask = self.create_task('macplist')
plisttask.set_inputs(self.link_task.outputs)
out = self.link_task.outputs[0]
self.mac_plist = self.mac_plist % (out.name)
name = bundle_name_for_output(out)
dir = self.create_bundle_dirs(name, out)
n1 = dir.find_or_declare(['Contents', 'Info.plist'])
plisttask.set_outputs([n1])
plisttask.mac_plist = self.mac_plist
plisttask.install_path = os.path.join(self.install_path, name, 'Contents')
self.plisttask = plisttask
@after('apply_link')
@feature('cshlib')
def apply_link_osx(self):
name = self.link_task.outputs[0].name
if not self.install_path:
return
if getattr(self, 'vnum', None):
name = name.replace('.dylib', '.%s.dylib' % self.vnum)
path = os.path.join(Utils.subst_vars(self.install_path, self.env), name)
if '-dynamiclib' in self.env['LINKFLAGS']:
self.env.append_value('LINKFLAGS', '-install_name')
self.env.append_value('LINKFLAGS', path)
@before('apply_link', 'apply_lib_vars')
@feature('cc', 'cxx')
def apply_bundle(self):
"""use env['MACBUNDLE'] to force all shlibs into mac bundles
or use obj.mac_bundle = True for specific targets only"""
if not ('cshlib' in self.features or 'shlib' in self.features): return
if self.env['MACBUNDLE'] or getattr(self, 'mac_bundle', False):
self.env['shlib_PATTERN'] = self.env['macbundle_PATTERN']
uselib = self.uselib = self.to_list(self.uselib)
if not 'MACBUNDLE' in uselib: uselib.append('MACBUNDLE')
@after('apply_link')
@feature('cshlib')
def apply_bundle_remove_dynamiclib(self):
if self.env['MACBUNDLE'] or getattr(self, 'mac_bundle', False):
if not getattr(self, 'vnum', None):
try:
self.env['LINKFLAGS'].remove('-dynamiclib')
except ValueError:
pass
# TODO REMOVE IN 1.6 (global variable)
app_dirs = ['Contents', 'Contents/MacOS', 'Contents/Resources']
def app_build(task):
env = task.env
shutil.copy2(task.inputs[0].srcpath(env), task.outputs[0].abspath(env))
return 0
def plist_build(task):
env = task.env
f = open(task.outputs[0].abspath(env), "w")
f.write(task.mac_plist)
f.close()
return 0
Task.task_type_from_func('macapp', vars=[], func=app_build, after="cxx_link cc_link static_link")
Task.task_type_from_func('macplist', vars=[], func=plist_build, after="cxx_link cc_link static_link")
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2006-2009 (ita)
"""
C/C++ preprocessor for finding dependencies
Reasons for using the Waf preprocessor by default
1. Some c/c++ extensions (Qt) require a custom preprocessor for obtaining the dependencies (.moc files)
2. Not all compilers provide .d files for obtaining the dependencies (portability)
3. A naive file scanner will not catch the constructs such as "#include foo()"
4. A naive file scanner will catch unnecessary dependencies (change an unused header -> recompile everything)
Regarding the speed concerns:
a. the preprocessing is performed only when files must be compiled
b. the macros are evaluated only for #if/#elif/#include
c. the time penalty is about 10%
d. system headers are not scanned
Now if you do not want the Waf preprocessor, the tool "gccdeps" uses the .d files produced
during the compilation to track the dependencies (useful when used with the boost libraries).
It only works with gcc though, and it cannot be used with Qt builds. A dumb
file scanner will be added in the future, so we will have most bahaviours.
"""
# TODO: more varargs, pragma once
# TODO: dumb file scanner tracking all includes
import re, sys, os, string
import Logs, Build, Utils
from Logs import debug, error
import traceback
class PreprocError(Utils.WafError):
pass
POPFILE = '-'
recursion_limit = 100
"do not loop too much on header inclusion"
go_absolute = 0
"set to 1 to track headers on files in /usr/include - else absolute paths are ignored"
standard_includes = ['/usr/include']
if sys.platform == "win32":
standard_includes = []
use_trigraphs = 0
'apply the trigraph rules first'
strict_quotes = 0
"Keep <> for system includes (do not search for those includes)"
g_optrans = {
'not':'!',
'and':'&&',
'bitand':'&',
'and_eq':'&=',
'or':'||',
'bitor':'|',
'or_eq':'|=',
'xor':'^',
'xor_eq':'^=',
'compl':'~',
}
"these ops are for c++, to reset, set an empty dict"
# ignore #warning and #error
re_lines = re.compile(\
'^[ \t]*(#|%:)[ \t]*(ifdef|ifndef|if|else|elif|endif|include|import|define|undef|pragma)[ \t]*(.*)\r*$',
re.IGNORECASE | re.MULTILINE)
re_mac = re.compile("^[a-zA-Z_]\w*")
re_fun = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*[(]')
re_pragma_once = re.compile('^\s*once\s*', re.IGNORECASE)
re_nl = re.compile('\\\\\r*\n', re.MULTILINE)
re_cpp = re.compile(\
r"""(/\*[^*]*\*+([^/*][^*]*\*+)*/)|//[^\n]*|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|.[^/"'\\]*)""",
re.MULTILINE)
trig_def = [('??'+a, b) for a, b in zip("=-/!'()<>", r'#~\|^[]{}')]
chr_esc = {'0':0, 'a':7, 'b':8, 't':9, 'n':10, 'f':11, 'v':12, 'r':13, '\\':92, "'":39}
NUM = 'i'
OP = 'O'
IDENT = 'T'
STR = 's'
CHAR = 'c'
tok_types = [NUM, STR, IDENT, OP]
exp_types = [
r"""0[xX](?P<hex>[a-fA-F0-9]+)(?P<qual1>[uUlL]*)|L*?'(?P<char>(\\.|[^\\'])+)'|(?P<n1>\d+)[Ee](?P<exp0>[+-]*?\d+)(?P<float0>[fFlL]*)|(?P<n2>\d*\.\d+)([Ee](?P<exp1>[+-]*?\d+))?(?P<float1>[fFlL]*)|(?P<n4>\d+\.\d*)([Ee](?P<exp2>[+-]*?\d+))?(?P<float2>[fFlL]*)|(?P<oct>0*)(?P<n0>\d+)(?P<qual2>[uUlL]*)""",
r'L?"([^"\\]|\\.)*"',
r'[a-zA-Z_]\w*',
r'%:%:|<<=|>>=|\.\.\.|<<|<%|<:|<=|>>|>=|\+\+|\+=|--|->|-=|\*=|/=|%:|%=|%>|==|&&|&=|\|\||\|=|\^=|:>|!=|##|[\(\)\{\}\[\]<>\?\|\^\*\+&=:!#;,%/\-\?\~\.]',
]
re_clexer = re.compile('|'.join(["(?P<%s>%s)" % (name, part) for name, part in zip(tok_types, exp_types)]), re.M)
accepted = 'a'
ignored = 'i'
undefined = 'u'
skipped = 's'
def repl(m):
s = m.group(1)
if s is not None: return ' '
s = m.group(3)
if s is None: return ''
return s
def filter_comments(filename):
# return a list of tuples : keyword, line
code = Utils.readf(filename)
if use_trigraphs:
for (a, b) in trig_def: code = code.split(a).join(b)
code = re_nl.sub('', code)
code = re_cpp.sub(repl, code)
return [(m.group(2), m.group(3)) for m in re.finditer(re_lines, code)]
prec = {}
# op -> number, needed for such expressions: #if 1 && 2 != 0
ops = ['* / %', '+ -', '<< >>', '< <= >= >', '== !=', '& | ^', '&& ||', ',']
for x in range(len(ops)):
syms = ops[x]
for u in syms.split():
prec[u] = x
def reduce_nums(val_1, val_2, val_op):
"""apply arithmetic rules and try to return an integer result"""
#print val_1, val_2, val_op
# now perform the operation, make certain a and b are numeric
try: a = 0 + val_1
except TypeError: a = int(val_1)
try: b = 0 + val_2
except TypeError: b = int(val_2)
d = val_op
if d == '%': c = a%b
elif d=='+': c = a+b
elif d=='-': c = a-b
elif d=='*': c = a*b
elif d=='/': c = a/b
elif d=='^': c = a^b
elif d=='|': c = a|b
elif d=='||': c = int(a or b)
elif d=='&': c = a&b
elif d=='&&': c = int(a and b)
elif d=='==': c = int(a == b)
elif d=='!=': c = int(a != b)
elif d=='<=': c = int(a <= b)
elif d=='<': c = int(a < b)
elif d=='>': c = int(a > b)
elif d=='>=': c = int(a >= b)
elif d=='^': c = int(a^b)
elif d=='<<': c = a<<b
elif d=='>>': c = a>>b
else: c = 0
return c
def get_num(lst):
if not lst: raise PreprocError("empty list for get_num")
(p, v) = lst[0]
if p == OP:
if v == '(':
count_par = 1
i = 1
while i < len(lst):
(p, v) = lst[i]
if p == OP:
if v == ')':
count_par -= 1
if count_par == 0:
break
elif v == '(':
count_par += 1
i += 1
else:
raise PreprocError("rparen expected %r" % lst)
(num, _) = get_term(lst[1:i])
return (num, lst[i+1:])
elif v == '+':
return get_num(lst[1:])
elif v == '-':
num, lst = get_num(lst[1:])
return (reduce_nums('-1', num, '*'), lst)
elif v == '!':
num, lst = get_num(lst[1:])
return (int(not int(num)), lst)
elif v == '~':
return (~ int(num), lst)
else:
raise PreprocError("invalid op token %r for get_num" % lst)
elif p == NUM:
return v, lst[1:]
elif p == IDENT:
# all macros should have been replaced, remaining identifiers eval to 0
return 0, lst[1:]
else:
raise PreprocError("invalid token %r for get_num" % lst)
def get_term(lst):
if not lst: raise PreprocError("empty list for get_term")
num, lst = get_num(lst)
if not lst:
return (num, [])
(p, v) = lst[0]
if p == OP:
if v == '&&' and not num:
return (num, [])
elif v == '||' and num:
return (num, [])
elif v == ',':
# skip
return get_term(lst[1:])
elif v == '?':
count_par = 0
i = 1
while i < len(lst):
(p, v) = lst[i]
if p == OP:
if v == ')':
count_par -= 1
elif v == '(':
count_par += 1
elif v == ':':
if count_par == 0:
break
i += 1
else:
raise PreprocError("rparen expected %r" % lst)
if int(num):
return get_term(lst[1:i])
else:
return get_term(lst[i+1:])
else:
num2, lst = get_num(lst[1:])
if not lst:
# no more tokens to process
num2 = reduce_nums(num, num2, v)
return get_term([(NUM, num2)] + lst)
# operator precedence
p2, v2 = lst[0]
if p2 != OP:
raise PreprocError("op expected %r" % lst)
if prec[v2] >= prec[v]:
num2 = reduce_nums(num, num2, v)
return get_term([(NUM, num2)] + lst)
else:
num3, lst = get_num(lst[1:])
num3 = reduce_nums(num2, num3, v2)
return get_term([(NUM, num), (p, v), (NUM, num3)] + lst)
raise PreprocError("cannot reduce %r" % lst)
def reduce_eval(lst):
"""take a list of tokens and output true or false (#if/#elif conditions)"""
num, lst = get_term(lst)
return (NUM, num)
def stringize(lst):
"""use for converting a list of tokens to a string"""
lst = [str(v2) for (p2, v2) in lst]
return "".join(lst)
def paste_tokens(t1, t2):
"""
here is what we can paste:
a ## b -> ab
> ## = -> >=
a ## 2 -> a2
"""
p1 = None
if t1[0] == OP and t2[0] == OP:
p1 = OP
elif t1[0] == IDENT and (t2[0] == IDENT or t2[0] == NUM):
p1 = IDENT
elif t1[0] == NUM and t2[0] == NUM:
p1 = NUM
if not p1:
raise PreprocError('tokens do not make a valid paste %r and %r' % (t1, t2))
return (p1, t1[1] + t2[1])
def reduce_tokens(lst, defs, ban=[]):
"""replace the tokens in lst, using the macros provided in defs, and a list of macros that cannot be re-applied"""
i = 0
while i < len(lst):
(p, v) = lst[i]
if p == IDENT and v == "defined":
del lst[i]
if i < len(lst):
(p2, v2) = lst[i]
if p2 == IDENT:
if v2 in defs:
lst[i] = (NUM, 1)
else:
lst[i] = (NUM, 0)
elif p2 == OP and v2 == '(':
del lst[i]
(p2, v2) = lst[i]
del lst[i] # remove the ident, and change the ) for the value
if v2 in defs:
lst[i] = (NUM, 1)
else:
lst[i] = (NUM, 0)
else:
raise PreprocError("invalid define expression %r" % lst)
elif p == IDENT and v in defs:
if isinstance(defs[v], str):
a, b = extract_macro(defs[v])
defs[v] = b
macro_def = defs[v]
to_add = macro_def[1]
if isinstance(macro_def[0], list):
# macro without arguments
del lst[i]
for x in xrange(len(to_add)):
lst.insert(i, to_add[x])
i += 1
else:
# collect the arguments for the funcall
args = []
del lst[i]
if i >= len(lst):
raise PreprocError("expected '(' after %r (got nothing)" % v)
(p2, v2) = lst[i]
if p2 != OP or v2 != '(':
raise PreprocError("expected '(' after %r" % v)
del lst[i]
one_param = []
count_paren = 0
while i < len(lst):
p2, v2 = lst[i]
del lst[i]
if p2 == OP and count_paren == 0:
if v2 == '(':
one_param.append((p2, v2))
count_paren += 1
elif v2 == ')':
if one_param: args.append(one_param)
break
elif v2 == ',':
if not one_param: raise PreprocError("empty param in funcall %s" % p)
args.append(one_param)
one_param = []
else:
one_param.append((p2, v2))
else:
one_param.append((p2, v2))
if v2 == '(': count_paren += 1
elif v2 == ')': count_paren -= 1
else:
raise PreprocError('malformed macro')
# substitute the arguments within the define expression
accu = []
arg_table = macro_def[0]
j = 0
while j < len(to_add):
(p2, v2) = to_add[j]
if p2 == OP and v2 == '#':
# stringize is for arguments only
if j+1 < len(to_add) and to_add[j+1][0] == IDENT and to_add[j+1][1] in arg_table:
toks = args[arg_table[to_add[j+1][1]]]
accu.append((STR, stringize(toks)))
j += 1
else:
accu.append((p2, v2))
elif p2 == OP and v2 == '##':
# token pasting, how can man invent such a complicated system?
if accu and j+1 < len(to_add):
# we have at least two tokens
t1 = accu[-1]
if to_add[j+1][0] == IDENT and to_add[j+1][1] in arg_table:
toks = args[arg_table[to_add[j+1][1]]]
if toks:
accu[-1] = paste_tokens(t1, toks[0]) #(IDENT, accu[-1][1] + toks[0][1])
accu.extend(toks[1:])
else:
# error, case "a##"
accu.append((p2, v2))
accu.extend(toks)
elif to_add[j+1][0] == IDENT and to_add[j+1][1] == '__VA_ARGS__':
# TODO not sure
# first collect the tokens
va_toks = []
st = len(macro_def[0])
pt = len(args)
for x in args[pt-st+1:]:
va_toks.extend(x)
va_toks.append((OP, ','))
if va_toks: va_toks.pop() # extra comma
if len(accu)>1:
(p3, v3) = accu[-1]
(p4, v4) = accu[-2]
if v3 == '##':
# remove the token paste
accu.pop()
if v4 == ',' and pt < st:
# remove the comma
accu.pop()
accu += va_toks
else:
accu[-1] = paste_tokens(t1, to_add[j+1])
j += 1
else:
# invalid paste, case "##a" or "b##"
accu.append((p2, v2))
elif p2 == IDENT and v2 in arg_table:
toks = args[arg_table[v2]]
reduce_tokens(toks, defs, ban+[v])
accu.extend(toks)
else:
accu.append((p2, v2))
j += 1
reduce_tokens(accu, defs, ban+[v])
for x in xrange(len(accu)-1, -1, -1):
lst.insert(i, accu[x])
i += 1
def eval_macro(lst, adefs):
"""reduce the tokens from the list lst, and try to return a 0/1 result"""
reduce_tokens(lst, adefs, [])
if not lst: raise PreprocError("missing tokens to evaluate")
(p, v) = reduce_eval(lst)
return int(v) != 0
def extract_macro(txt):
"""process a macro definition from "#define f(x, y) x * y" into a function or a simple macro without arguments"""
t = tokenize(txt)
if re_fun.search(txt):
p, name = t[0]
p, v = t[1]
if p != OP: raise PreprocError("expected open parenthesis")
i = 1
pindex = 0
params = {}
prev = '('
while 1:
i += 1
p, v = t[i]
if prev == '(':
if p == IDENT:
params[v] = pindex
pindex += 1
prev = p
elif p == OP and v == ')':
break
else:
raise PreprocError("unexpected token (3)")
elif prev == IDENT:
if p == OP and v == ',':
prev = v
elif p == OP and v == ')':
break
else:
raise PreprocError("comma or ... expected")
elif prev == ',':
if p == IDENT:
params[v] = pindex
pindex += 1
prev = p
elif p == OP and v == '...':
raise PreprocError("not implemented (1)")
else:
raise PreprocError("comma or ... expected (2)")
elif prev == '...':
raise PreprocError("not implemented (2)")
else:
raise PreprocError("unexpected else")
#~ print (name, [params, t[i+1:]])
return (name, [params, t[i+1:]])
else:
(p, v) = t[0]
return (v, [[], t[1:]])
re_include = re.compile('^\s*(<(?P<a>.*)>|"(?P<b>.*)")')
def extract_include(txt, defs):
"""process a line in the form "#include foo" to return a string representing the file"""
m = re_include.search(txt)
if m:
if m.group('a'): return '<', m.group('a')
if m.group('b'): return '"', m.group('b')
# perform preprocessing and look at the result, it must match an include
toks = tokenize(txt)
reduce_tokens(toks, defs, ['waf_include'])
if not toks:
raise PreprocError("could not parse include %s" % txt)
if len(toks) == 1:
if toks[0][0] == STR:
return '"', toks[0][1]
else:
if toks[0][1] == '<' and toks[-1][1] == '>':
return stringize(toks).lstrip('<').rstrip('>')
raise PreprocError("could not parse include %s." % txt)
def parse_char(txt):
if not txt: raise PreprocError("attempted to parse a null char")
if txt[0] != '\\':
return ord(txt)
c = txt[1]
if c == 'x':
if len(txt) == 4 and txt[3] in string.hexdigits: return int(txt[2:], 16)
return int(txt[2:], 16)
elif c.isdigit():
if c == '0' and len(txt)==2: return 0
for i in 3, 2, 1:
if len(txt) > i and txt[1:1+i].isdigit():
return (1+i, int(txt[1:1+i], 8))
else:
try: return chr_esc[c]
except KeyError: raise PreprocError("could not parse char literal '%s'" % txt)
def tokenize(s):
"""convert a string into a list of tokens (shlex.split does not apply to c/c++/d)"""
ret = []
for match in re_clexer.finditer(s):
m = match.group
for name in tok_types:
v = m(name)
if v:
if name == IDENT:
try: v = g_optrans[v]; name = OP
except KeyError:
# c++ specific
if v.lower() == "true":
v = 1
name = NUM
elif v.lower() == "false":
v = 0
name = NUM
elif name == NUM:
if m('oct'): v = int(v, 8)
elif m('hex'): v = int(m('hex'), 16)
elif m('n0'): v = m('n0')
else:
v = m('char')
if v: v = parse_char(v)
else: v = m('n2') or m('n4')
elif name == OP:
if v == '%:': v = '#'
elif v == '%:%:': v = '##'
elif name == STR:
# remove the quotes around the string
v = v[1:-1]
ret.append((name, v))
break
return ret
class c_parser(object):
def __init__(self, nodepaths=None, defines=None):
#self.lines = txt.split('\n')
self.lines = []
if defines is None:
self.defs = {}
else:
self.defs = dict(defines) # make a copy
self.state = []
self.env = None # needed for the variant when searching for files
self.count_files = 0
self.currentnode_stack = []
self.nodepaths = nodepaths or []
self.nodes = []
self.names = []
# file added
self.curfile = ''
self.ban_includes = []
def tryfind(self, filename):
self.curfile = filename
# for msvc it should be a for loop on the whole stack
found = self.currentnode_stack[-1].find_resource(filename)
for n in self.nodepaths:
if found:
break
found = n.find_resource(filename)
if not found:
if not filename in self.names:
self.names.append(filename)
else:
self.nodes.append(found)
if filename[-4:] != '.moc':
self.addlines(found)
return found
def addlines(self, node):
self.currentnode_stack.append(node.parent)
filepath = node.abspath(self.env)
self.count_files += 1
if self.count_files > recursion_limit: raise PreprocError("recursion limit exceeded")
pc = self.parse_cache
debug('preproc: reading file %r', filepath)
try:
lns = pc[filepath]
except KeyError:
pass
else:
self.lines = lns + self.lines
return
try:
lines = filter_comments(filepath)
lines.append((POPFILE, ''))
pc[filepath] = lines # cache the lines filtered
self.lines = lines + self.lines
except IOError:
raise PreprocError("could not read the file %s" % filepath)
except Exception:
if Logs.verbose > 0:
error("parsing %s failed" % filepath)
traceback.print_exc()
def start(self, node, env):
debug('preproc: scanning %s (in %s)', node.name, node.parent.name)
self.env = env
variant = node.variant(env)
bld = node.__class__.bld
try:
self.parse_cache = bld.parse_cache
except AttributeError:
bld.parse_cache = {}
self.parse_cache = bld.parse_cache
self.addlines(node)
if env['DEFLINES']:
self.lines = [('define', x) for x in env['DEFLINES']] + self.lines
while self.lines:
(kind, line) = self.lines.pop(0)
if kind == POPFILE:
self.currentnode_stack.pop()
continue
try:
self.process_line(kind, line)
except Exception, e:
if Logs.verbose:
debug('preproc: line parsing failed (%s): %s %s', e, line, Utils.ex_stack())
def process_line(self, token, line):
ve = Logs.verbose
if ve: debug('preproc: line is %s - %s state is %s', token, line, self.state)
state = self.state
# make certain we define the state if we are about to enter in an if block
if token in ['ifdef', 'ifndef', 'if']:
state.append(undefined)
elif token == 'endif':
state.pop()
# skip lines when in a dead 'if' branch, wait for the endif
if not token in ['else', 'elif', 'endif']:
if skipped in self.state or ignored in self.state:
return
if token == 'if':
ret = eval_macro(tokenize(line), self.defs)
if ret: state[-1] = accepted
else: state[-1] = ignored
elif token == 'ifdef':
m = re_mac.search(line)
if m and m.group(0) in self.defs: state[-1] = accepted
else: state[-1] = ignored
elif token == 'ifndef':
m = re_mac.search(line)
if m and m.group(0) in self.defs: state[-1] = ignored
else: state[-1] = accepted
elif token == 'include' or token == 'import':
(kind, inc) = extract_include(line, self.defs)
if inc in self.ban_includes: return
if token == 'import': self.ban_includes.append(inc)
if ve: debug('preproc: include found %s (%s) ', inc, kind)
if kind == '"' or not strict_quotes:
self.tryfind(inc)
elif token == 'elif':
if state[-1] == accepted:
state[-1] = skipped
elif state[-1] == ignored:
if eval_macro(tokenize(line), self.defs):
state[-1] = accepted
elif token == 'else':
if state[-1] == accepted: state[-1] = skipped
elif state[-1] == ignored: state[-1] = accepted
elif token == 'define':
m = re_mac.search(line)
if m:
name = m.group(0)
if ve: debug('preproc: define %s %s', name, line)
self.defs[name] = line
else:
raise PreprocError("invalid define line %s" % line)
elif token == 'undef':
m = re_mac.search(line)
if m and m.group(0) in self.defs:
self.defs.__delitem__(m.group(0))
#print "undef %s" % name
elif token == 'pragma':
if re_pragma_once.search(line.lower()):
self.ban_includes.append(self.curfile)
def get_deps(node, env, nodepaths=[]):
"""
Get the dependencies using a c/c++ preprocessor, this is required for finding dependencies of the kind
#include some_macro()
"""
gruik = c_parser(nodepaths)
gruik.start(node, env)
return (gruik.nodes, gruik.names)
#################### dumb dependency scanner
re_inc = re.compile(\
'^[ \t]*(#|%:)[ \t]*(include)[ \t]*(.*)\r*$',
re.IGNORECASE | re.MULTILINE)
def lines_includes(filename):
code = Utils.readf(filename)
if use_trigraphs:
for (a, b) in trig_def: code = code.split(a).join(b)
code = re_nl.sub('', code)
code = re_cpp.sub(repl, code)
return [(m.group(2), m.group(3)) for m in re.finditer(re_inc, code)]
def get_deps_simple(node, env, nodepaths=[], defines={}):
"""
Get the dependencies by just looking recursively at the #include statements
"""
nodes = []
names = []
def find_deps(node):
lst = lines_includes(node.abspath(env))
for (_, line) in lst:
(t, filename) = extract_include(line, defines)
if filename in names:
continue
if filename.endswith('.moc'):
names.append(filename)
found = None
for n in nodepaths:
if found:
break
found = n.find_resource(filename)
if not found:
if not filename in names:
names.append(filename)
elif not found in nodes:
nodes.append(found)
find_deps(node)
find_deps(node)
return (nodes, names)
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2007 (ita)
# Gustavo Carneiro (gjc), 2007
"Python support"
import os, sys
import TaskGen, Utils, Utils, Runner, Options, Build
from Logs import debug, warn, info
from TaskGen import extension, taskgen, before, after, feature
from Configure import conf
EXT_PY = ['.py']
FRAG_2 = '''
#ifdef __cplusplus
extern "C" {
#endif
void Py_Initialize(void);
void Py_Finalize(void);
#ifdef __cplusplus
}
#endif
int main()
{
Py_Initialize();
Py_Finalize();
return 0;
}
'''
@before('apply_incpaths', 'apply_lib_vars', 'apply_type_vars')
@feature('pyext')
@before('apply_bundle')
def init_pyext(self):
self.default_install_path = '${PYTHONDIR}'
self.uselib = self.to_list(getattr(self, 'uselib', ''))
if not 'PYEXT' in self.uselib:
self.uselib.append('PYEXT')
self.env['MACBUNDLE'] = True
@before('apply_link', 'apply_lib_vars', 'apply_type_vars')
@after('apply_bundle')
@feature('pyext')
def pyext_shlib_ext(self):
# override shlib_PATTERN set by the osx module
self.env['shlib_PATTERN'] = self.env['pyext_PATTERN']
@before('apply_incpaths', 'apply_lib_vars', 'apply_type_vars')
@feature('pyembed')
def init_pyembed(self):
self.uselib = self.to_list(getattr(self, 'uselib', ''))
if not 'PYEMBED' in self.uselib:
self.uselib.append('PYEMBED')
@extension(EXT_PY)
def process_py(self, node):
if not (self.bld.is_install and self.install_path):
return
def inst_py(ctx):
install_pyfile(self, node)
self.bld.add_post_fun(inst_py)
def install_pyfile(self, node):
path = self.bld.get_install_path(self.install_path + os.sep + node.name, self.env)
self.bld.install_files(self.install_path, [node], self.env, self.chmod, postpone=False)
if self.bld.is_install < 0:
info("* removing byte compiled python files")
for x in 'co':
try:
os.remove(path + x)
except OSError:
pass
if self.bld.is_install > 0:
if self.env['PYC'] or self.env['PYO']:
info("* byte compiling %r" % path)
if self.env['PYC']:
program = ("""
import sys, py_compile
for pyfile in sys.argv[1:]:
py_compile.compile(pyfile, pyfile + 'c')
""")
argv = [self.env['PYTHON'], '-c', program, path]
ret = Utils.pproc.Popen(argv).wait()
if ret:
raise Utils.WafError('bytecode compilation failed %r' % path)
if self.env['PYO']:
program = ("""
import sys, py_compile
for pyfile in sys.argv[1:]:
py_compile.compile(pyfile, pyfile + 'o')
""")
argv = [self.env['PYTHON'], self.env['PYFLAGS_OPT'], '-c', program, path]
ret = Utils.pproc.Popen(argv).wait()
if ret:
raise Utils.WafError('bytecode compilation failed %r' % path)
# COMPAT
class py_taskgen(TaskGen.task_gen):
def __init__(self, *k, **kw):
TaskGen.task_gen.__init__(self, *k, **kw)
@before('apply_core')
@after('vars_target_cprogram', 'vars_target_cstaticlib')
@feature('py')
def init_py(self):
self.default_install_path = '${PYTHONDIR}'
def _get_python_variables(python_exe, variables, imports=['import sys']):
"""Run a python interpreter and print some variables"""
program = list(imports)
program.append('')
for v in variables:
program.append("print(repr(%s))" % v)
os_env = dict(os.environ)
try:
del os_env['MACOSX_DEPLOYMENT_TARGET'] # see comments in the OSX tool
except KeyError:
pass
proc = Utils.pproc.Popen([python_exe, "-c", '\n'.join(program)], stdout=Utils.pproc.PIPE, env=os_env)
output = proc.communicate()[0].split("\n") # do not touch, python3
if proc.returncode:
if Options.options.verbose:
warn("Python program to extract python configuration variables failed:\n%s"
% '\n'.join(["line %03i: %s" % (lineno+1, line) for lineno, line in enumerate(program)]))
raise RuntimeError
return_values = []
for s in output:
s = s.strip()
if not s:
continue
if s == 'None':
return_values.append(None)
elif s[0] == "'" and s[-1] == "'":
return_values.append(s[1:-1])
elif s[0].isdigit():
return_values.append(int(s))
else: break
return return_values
@conf
def check_python_headers(conf):
"""Check for headers and libraries necessary to extend or embed python.
On success the environment variables xxx_PYEXT and xxx_PYEMBED are added for uselib
PYEXT: for compiling python extensions
PYEMBED: for embedding a python interpreter"""
if not conf.env['CC_NAME'] and not conf.env['CXX_NAME']:
conf.fatal('load a compiler first (gcc, g++, ..)')
if not conf.env['PYTHON_VERSION']:
conf.check_python_version()
env = conf.env
python = env['PYTHON']
if not python:
conf.fatal('could not find the python executable')
## On Mac OSX we need to use mac bundles for python plugins
if Options.platform == 'darwin':
conf.check_tool('osx')
try:
# Get some python configuration variables using distutils
v = 'prefix SO SYSLIBS LDFLAGS SHLIBS LIBDIR LIBPL INCLUDEPY Py_ENABLE_SHARED MACOSX_DEPLOYMENT_TARGET'.split()
(python_prefix, python_SO, python_SYSLIBS, python_LDFLAGS, python_SHLIBS,
python_LIBDIR, python_LIBPL, INCLUDEPY, Py_ENABLE_SHARED,
python_MACOSX_DEPLOYMENT_TARGET) = \
_get_python_variables(python, ["get_config_var('%s')" % x for x in v],
['from distutils.sysconfig import get_config_var'])
except RuntimeError:
conf.fatal("Python development headers not found (-v for details).")
conf.log.write("""Configuration returned from %r:
python_prefix = %r
python_SO = %r
python_SYSLIBS = %r
python_LDFLAGS = %r
python_SHLIBS = %r
python_LIBDIR = %r
python_LIBPL = %r
INCLUDEPY = %r
Py_ENABLE_SHARED = %r
MACOSX_DEPLOYMENT_TARGET = %r
""" % (python, python_prefix, python_SO, python_SYSLIBS, python_LDFLAGS, python_SHLIBS,
python_LIBDIR, python_LIBPL, INCLUDEPY, Py_ENABLE_SHARED, python_MACOSX_DEPLOYMENT_TARGET))
if python_MACOSX_DEPLOYMENT_TARGET:
conf.env['MACOSX_DEPLOYMENT_TARGET'] = python_MACOSX_DEPLOYMENT_TARGET
conf.environ['MACOSX_DEPLOYMENT_TARGET'] = python_MACOSX_DEPLOYMENT_TARGET
env['pyext_PATTERN'] = '%s'+python_SO
# Check for python libraries for embedding
if python_SYSLIBS is not None:
for lib in python_SYSLIBS.split():
if lib.startswith('-l'):
lib = lib[2:] # strip '-l'
env.append_value('LIB_PYEMBED', lib)
if python_SHLIBS is not None:
for lib in python_SHLIBS.split():
if lib.startswith('-l'):
env.append_value('LIB_PYEMBED', lib[2:]) # strip '-l'
else:
env.append_value('LINKFLAGS_PYEMBED', lib)
if Options.platform != 'darwin' and python_LDFLAGS:
env.append_value('LINKFLAGS_PYEMBED', python_LDFLAGS.split())
result = False
name = 'python' + env['PYTHON_VERSION']
if python_LIBDIR is not None:
path = [python_LIBDIR]
conf.log.write("\n\n# Trying LIBDIR: %r\n" % path)
result = conf.check(lib=name, uselib='PYEMBED', libpath=path)
if not result and python_LIBPL is not None:
conf.log.write("\n\n# try again with -L$python_LIBPL (some systems don't install the python library in $prefix/lib)\n")
path = [python_LIBPL]
result = conf.check(lib=name, uselib='PYEMBED', libpath=path)
if not result:
conf.log.write("\n\n# try again with -L$prefix/libs, and pythonXY name rather than pythonX.Y (win32)\n")
path = [os.path.join(python_prefix, "libs")]
name = 'python' + env['PYTHON_VERSION'].replace('.', '')
result = conf.check(lib=name, uselib='PYEMBED', libpath=path)
if result:
env['LIBPATH_PYEMBED'] = path
env.append_value('LIB_PYEMBED', name)
else:
conf.log.write("\n\n### LIB NOT FOUND\n")
# under certain conditions, python extensions must link to
# python libraries, not just python embedding programs.
if (sys.platform == 'win32' or sys.platform.startswith('os2')
or sys.platform == 'darwin' or Py_ENABLE_SHARED):
env['LIBPATH_PYEXT'] = env['LIBPATH_PYEMBED']
env['LIB_PYEXT'] = env['LIB_PYEMBED']
# We check that pythonX.Y-config exists, and if it exists we
# use it to get only the includes, else fall back to distutils.
python_config = conf.find_program(
'python%s-config' % ('.'.join(env['PYTHON_VERSION'].split('.')[:2])),
var='PYTHON_CONFIG')
if not python_config:
python_config = conf.find_program(
'python-config-%s' % ('.'.join(env['PYTHON_VERSION'].split('.')[:2])),
var='PYTHON_CONFIG')
includes = []
if python_config:
for incstr in Utils.cmd_output("%s %s --includes" % (python, python_config)).strip().split():
# strip the -I or /I
if (incstr.startswith('-I')
or incstr.startswith('/I')):
incstr = incstr[2:]
# append include path, unless already given
if incstr not in includes:
includes.append(incstr)
conf.log.write("Include path for Python extensions "
"(found via python-config --includes): %r\n" % (includes,))
env['CPPPATH_PYEXT'] = includes
env['CPPPATH_PYEMBED'] = includes
else:
conf.log.write("Include path for Python extensions "
"(found via distutils module): %r\n" % (INCLUDEPY,))
env['CPPPATH_PYEXT'] = [INCLUDEPY]
env['CPPPATH_PYEMBED'] = [INCLUDEPY]
# Code using the Python API needs to be compiled with -fno-strict-aliasing
if env['CC_NAME'] == 'gcc':
env.append_value('CCFLAGS_PYEMBED', '-fno-strict-aliasing')
env.append_value('CCFLAGS_PYEXT', '-fno-strict-aliasing')
if env['CXX_NAME'] == 'gcc':
env.append_value('CXXFLAGS_PYEMBED', '-fno-strict-aliasing')
env.append_value('CXXFLAGS_PYEXT', '-fno-strict-aliasing')
# See if it compiles
conf.check(header_name='Python.h', define_name='HAVE_PYTHON_H',
uselib='PYEMBED', fragment=FRAG_2,
errmsg='Could not find the python development headers', mandatory=1)
@conf
def check_python_version(conf, minver=None):
"""
Check if the python interpreter is found matching a given minimum version.
minver should be a tuple, eg. to check for python >= 2.4.2 pass (2,4,2) as minver.
If successful, PYTHON_VERSION is defined as 'MAJOR.MINOR'
(eg. '2.4') of the actual python version found, and PYTHONDIR is
defined, pointing to the site-packages directory appropriate for
this python version, where modules/packages/extensions should be
installed.
"""
assert minver is None or isinstance(minver, tuple)
python = conf.env['PYTHON']
if not python:
conf.fatal('could not find the python executable')
# Get python version string
cmd = [python, "-c", "import sys\nfor x in sys.version_info: print(str(x))"]
debug('python: Running python command %r' % cmd)
proc = Utils.pproc.Popen(cmd, stdout=Utils.pproc.PIPE)
lines = proc.communicate()[0].split()
assert len(lines) == 5, "found %i lines, expected 5: %r" % (len(lines), lines)
pyver_tuple = (int(lines[0]), int(lines[1]), int(lines[2]), lines[3], int(lines[4]))
# compare python version with the minimum required
result = (minver is None) or (pyver_tuple >= minver)
if result:
# define useful environment variables
pyver = '.'.join([str(x) for x in pyver_tuple[:2]])
conf.env['PYTHON_VERSION'] = pyver
if 'PYTHONDIR' in conf.environ:
pydir = conf.environ['PYTHONDIR']
else:
if sys.platform == 'win32':
(python_LIBDEST, pydir) = \
_get_python_variables(python,
["get_config_var('LIBDEST')",
"get_python_lib(standard_lib=0, prefix=%r)" % conf.env['PREFIX']],
['from distutils.sysconfig import get_config_var, get_python_lib'])
else:
python_LIBDEST = None
(pydir,) = \
_get_python_variables(python,
["get_python_lib(standard_lib=0, prefix=%r)" % conf.env['PREFIX']],
['from distutils.sysconfig import get_config_var, get_python_lib'])
if python_LIBDEST is None:
if conf.env['LIBDIR']:
python_LIBDEST = os.path.join(conf.env['LIBDIR'], "python" + pyver)
else:
python_LIBDEST = os.path.join(conf.env['PREFIX'], "lib", "python" + pyver)
if hasattr(conf, 'define'): # conf.define is added by the C tool, so may not exist
conf.define('PYTHONDIR', pydir)
conf.env['PYTHONDIR'] = pydir
# Feedback
pyver_full = '.'.join(map(str, pyver_tuple[:3]))
if minver is None:
conf.check_message_custom('Python version', '', pyver_full)
else:
minver_str = '.'.join(map(str, minver))
conf.check_message('Python version', ">= %s" % minver_str, result, option=pyver_full)
if not result:
conf.fatal('The python version is too old (%r)' % pyver_full)
@conf
def check_python_module(conf, module_name):
"""
Check if the selected python interpreter can import the given python module.
"""
result = not Utils.pproc.Popen([conf.env['PYTHON'], "-c", "import %s" % module_name],
stderr=Utils.pproc.PIPE, stdout=Utils.pproc.PIPE).wait()
conf.check_message('Python module', module_name, result)
if not result:
conf.fatal('Could not find the python module %r' % module_name)
def detect(conf):
if not conf.env.PYTHON:
conf.env.PYTHON = sys.executable
python = conf.find_program('python', var='PYTHON')
if not python:
conf.fatal('Could not find the path of the python executable')
v = conf.env
v['PYCMD'] = '"import sys, py_compile;py_compile.compile(sys.argv[1], sys.argv[2])"'
v['PYFLAGS'] = ''
v['PYFLAGS_OPT'] = '-O'
v['PYC'] = getattr(Options.options, 'pyc', 1)
v['PYO'] = getattr(Options.options, 'pyo', 1)
def set_options(opt):
opt.add_option('--nopyc',
action='store_false',
default=1,
help = 'Do not install bytecode compiled .pyc files (configuration) [Default:install]',
dest = 'pyc')
opt.add_option('--nopyo',
action='store_false',
default=1,
help='Do not install optimised compiled .pyo files (configuration) [Default:install]',
dest='pyo')
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2006 (ita)
# Ralf Habacker, 2006 (rh)
import os, optparse
import Utils, Options, Configure
import ccroot, ar
from Configure import conftest
@conftest
def find_scc(conf):
v = conf.env
cc = None
if v['CC']: cc = v['CC']
elif 'CC' in conf.environ: cc = conf.environ['CC']
#if not cc: cc = conf.find_program('gcc', var='CC')
if not cc: cc = conf.find_program('cc', var='CC')
if not cc: conf.fatal('suncc was not found')
cc = conf.cmd_to_list(cc)
try:
if not Utils.cmd_output(cc + ['-flags']):
conf.fatal('suncc %r was not found' % cc)
except ValueError:
conf.fatal('suncc -flags could not be executed')
v['CC'] = cc
v['CC_NAME'] = 'sun'
@conftest
def scc_common_flags(conf):
v = conf.env
# CPPFLAGS CCDEFINES _CCINCFLAGS _CCDEFFLAGS
v['CC_SRC_F'] = ''
v['CC_TGT_F'] = ['-c', '-o', '']
v['CPPPATH_ST'] = '-I%s' # template for adding include paths
# linker
if not v['LINK_CC']: v['LINK_CC'] = v['CC']
v['CCLNK_SRC_F'] = ''
v['CCLNK_TGT_F'] = ['-o', ''] # solaris hack, separate the -o from the target
v['LIB_ST'] = '-l%s' # template for adding libs
v['LIBPATH_ST'] = '-L%s' # template for adding libpaths
v['STATICLIB_ST'] = '-l%s'
v['STATICLIBPATH_ST'] = '-L%s'
v['CCDEFINES_ST'] = '-D%s'
v['SONAME_ST'] = '-Wl,-h -Wl,%s'
v['SHLIB_MARKER'] = '-Bdynamic'
v['STATICLIB_MARKER'] = '-Bstatic'
# program
v['program_PATTERN'] = '%s'
# shared library
v['shlib_CCFLAGS'] = ['-Kpic', '-DPIC']
v['shlib_LINKFLAGS'] = ['-G']
v['shlib_PATTERN'] = 'lib%s.so'
# static lib
v['staticlib_LINKFLAGS'] = ['-Bstatic']
v['staticlib_PATTERN'] = 'lib%s.a'
detect = '''
find_scc
find_cpp
find_ar
scc_common_flags
cc_load_tools
cc_add_flags
link_add_flags
'''
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2006 (ita)
# Ralf Habacker, 2006 (rh)
import os, optparse
import Utils, Options, Configure
import ccroot, ar
from Configure import conftest
@conftest
def find_sxx(conf):
v = conf.env
cc = None
if v['CXX']: cc = v['CXX']
elif 'CXX' in conf.environ: cc = conf.environ['CXX']
if not cc: cc = conf.find_program('c++', var='CXX')
if not cc: conf.fatal('sunc++ was not found')
cc = conf.cmd_to_list(cc)
try:
if not Utils.cmd_output(cc + ['-flags']):
conf.fatal('sunc++ %r was not found' % cc)
except ValueError:
conf.fatal('sunc++ -flags could not be executed')
v['CXX'] = cc
v['CXX_NAME'] = 'sun'
@conftest
def sxx_common_flags(conf):
v = conf.env
# CPPFLAGS CXXDEFINES _CXXINCFLAGS _CXXDEFFLAGS
v['CXX_SRC_F'] = ''
v['CXX_TGT_F'] = ['-c', '-o', '']
v['CPPPATH_ST'] = '-I%s' # template for adding include paths
# linker
if not v['LINK_CXX']: v['LINK_CXX'] = v['CXX']
v['CXXLNK_SRC_F'] = ''
v['CXXLNK_TGT_F'] = ['-o', ''] # solaris hack, separate the -o from the target
v['LIB_ST'] = '-l%s' # template for adding libs
v['LIBPATH_ST'] = '-L%s' # template for adding libpaths
v['STATICLIB_ST'] = '-l%s'
v['STATICLIBPATH_ST'] = '-L%s'
v['CXXDEFINES_ST'] = '-D%s'
v['SONAME_ST'] = '-Wl,-h -Wl,%s'
v['SHLIB_MARKER'] = '-Bdynamic'
v['STATICLIB_MARKER'] = '-Bstatic'
# program
v['program_PATTERN'] = '%s'
# shared library
v['shlib_CXXFLAGS'] = ['-Kpic', '-DPIC']
v['shlib_LINKFLAGS'] = ['-G']
v['shlib_PATTERN'] = 'lib%s.so'
# static lib
v['staticlib_LINKFLAGS'] = ['-Bstatic']
v['staticlib_PATTERN'] = 'lib%s.a'
detect = '''
find_sxx
find_cpp
find_ar
sxx_common_flags
cxx_load_tools
cxx_add_flags
'''
#!/usr/bin/env python
# encoding: utf-8
# Carlos Rafael Giani, 2006
"""
Unit tests run in the shutdown() method, and for c/c++ programs
One should NOT have to give parameters to programs to execute
In the shutdown method, add the following code:
>>> def shutdown():
... ut = UnitTest.unit_test()
... ut.run()
... ut.print_results()
Each object to use as a unit test must be a program and must have X{obj.unit_test=1}
"""
import os, sys
import Build, TaskGen, Utils, Options, Logs, Task
from TaskGen import before, after, feature
from Constants import *
class unit_test(object):
"Unit test representation"
def __init__(self):
self.returncode_ok = 0 # Unit test returncode considered OK. All returncodes differing from this one
# will cause the unit test to be marked as "FAILED".
# The following variables are filled with data by run().
# print_results() uses these for printing the unit test summary,
# but if there is need for direct access to the results,
# they can be retrieved here, after calling run().
self.num_tests_ok = 0 # Number of successful unit tests
self.num_tests_failed = 0 # Number of failed unit tests
self.num_tests_err = 0 # Tests that have not even run
self.total_num_tests = 0 # Total amount of unit tests
self.max_label_length = 0 # Maximum label length (pretty-print the output)
self.unit_tests = Utils.ordered_dict() # Unit test dictionary. Key: the label (unit test filename relative
# to the build dir), value: unit test filename with absolute path
self.unit_test_results = {} # Dictionary containing the unit test results.
# Key: the label, value: result (true = success false = failure)
self.unit_test_erroneous = {} # Dictionary indicating erroneous unit tests.
# Key: the label, value: true = unit test has an error false = unit test is ok
self.change_to_testfile_dir = False #True if the test file needs to be executed from the same dir
self.want_to_see_test_output = False #True to see the stdout from the testfile (for example check suites)
self.want_to_see_test_error = False #True to see the stderr from the testfile (for example check suites)
self.run_if_waf_does = 'check' #build was the old default
def run(self):
"Run the unit tests and gather results (note: no output here)"
self.num_tests_ok = 0
self.num_tests_failed = 0
self.num_tests_err = 0
self.total_num_tests = 0
self.max_label_length = 0
self.unit_tests = Utils.ordered_dict()
self.unit_test_results = {}
self.unit_test_erroneous = {}
ld_library_path = []
# If waf is not building, don't run anything
if not Options.commands[self.run_if_waf_does]: return
# Get the paths for the shared libraries, and obtain the unit tests to execute
for obj in Build.bld.all_task_gen:
try:
link_task = obj.link_task
except AttributeError:
pass
else:
lib_path = link_task.outputs[0].parent.abspath(obj.env)
if lib_path not in ld_library_path:
ld_library_path.append(lib_path)
unit_test = getattr(obj, 'unit_test', '')
if unit_test and 'cprogram' in obj.features:
try:
output = obj.path
filename = os.path.join(output.abspath(obj.env), obj.target)
srcdir = output.abspath()
label = os.path.join(output.bldpath(obj.env), obj.target)
self.max_label_length = max(self.max_label_length, len(label))
self.unit_tests[label] = (filename, srcdir)
except KeyError:
pass
self.total_num_tests = len(self.unit_tests)
# Now run the unit tests
Utils.pprint('GREEN', 'Running the unit tests')
count = 0
result = 1
for label in self.unit_tests.allkeys:
file_and_src = self.unit_tests[label]
filename = file_and_src[0]
srcdir = file_and_src[1]
count += 1
line = Build.bld.progress_line(count, self.total_num_tests, Logs.colors.GREEN, Logs.colors.NORMAL)
if Options.options.progress_bar and line:
sys.stderr.write(line)
sys.stderr.flush()
try:
kwargs = {}
kwargs['env'] = os.environ.copy()
if self.change_to_testfile_dir:
kwargs['cwd'] = srcdir
if not self.want_to_see_test_output:
kwargs['stdout'] = Utils.pproc.PIPE # PIPE for ignoring output
if not self.want_to_see_test_error:
kwargs['stderr'] = Utils.pproc.PIPE # PIPE for ignoring output
if ld_library_path:
v = kwargs['env']
def add_path(dct, path, var):
dct[var] = os.pathsep.join(Utils.to_list(path) + [os.environ.get(var, '')])
if sys.platform == 'win32':
add_path(v, ld_library_path, 'PATH')
elif sys.platform == 'darwin':
add_path(v, ld_library_path, 'DYLD_LIBRARY_PATH')
add_path(v, ld_library_path, 'LD_LIBRARY_PATH')
else:
add_path(v, ld_library_path, 'LD_LIBRARY_PATH')
pp = Utils.pproc.Popen(filename, **kwargs)
pp.wait()
result = int(pp.returncode == self.returncode_ok)
if result:
self.num_tests_ok += 1
else:
self.num_tests_failed += 1
self.unit_test_results[label] = result
self.unit_test_erroneous[label] = 0
except OSError:
self.unit_test_erroneous[label] = 1
self.num_tests_err += 1
except KeyboardInterrupt:
pass
if Options.options.progress_bar: sys.stdout.write(Logs.colors.cursor_on)
def print_results(self):
"Pretty-prints a summary of all unit tests, along with some statistics"
# If waf is not building, don't output anything
if not Options.commands[self.run_if_waf_does]: return
p = Utils.pprint
# Early quit if no tests were performed
if self.total_num_tests == 0:
p('YELLOW', 'No unit tests present')
return
for label in self.unit_tests.allkeys:
filename = self.unit_tests[label]
err = 0
result = 0
try: err = self.unit_test_erroneous[label]
except KeyError: pass
try: result = self.unit_test_results[label]
except KeyError: pass
n = self.max_label_length - len(label)
if err: n += 4
elif result: n += 7
else: n += 3
line = '%s %s' % (label, '.' * n)
if err: p('RED', '%sERROR' % line)
elif result: p('GREEN', '%sOK' % line)
else: p('YELLOW', '%sFAILED' % line)
percentage_ok = float(self.num_tests_ok) / float(self.total_num_tests) * 100.0
percentage_failed = float(self.num_tests_failed) / float(self.total_num_tests) * 100.0
percentage_erroneous = float(self.num_tests_err) / float(self.total_num_tests) * 100.0
p('NORMAL', '''
Successful tests: %i (%.1f%%)
Failed tests: %i (%.1f%%)
Erroneous tests: %i (%.1f%%)
Total number of tests: %i
''' % (self.num_tests_ok, percentage_ok, self.num_tests_failed, percentage_failed,
self.num_tests_err, percentage_erroneous, self.total_num_tests))
p('GREEN', 'Unit tests finished')
############################################################################################
"""
New unit test system
The targets with feature 'test' are executed after they are built
bld(features='cprogram cc test', ...)
To display the results:
import UnitTest
bld.add_post_fun(UnitTest.summary)
"""
import threading
testlock = threading.Lock()
def set_options(opt):
opt.add_option('--alltests', action='store_true', default=True, help='Exec all unit tests', dest='all_tests')
@feature('test')
@after('apply_link', 'vars_target_cprogram')
def make_test(self):
if not 'cprogram' in self.features:
Logs.error('test cannot be executed %s' % self)
return
self.default_install_path = None
self.create_task('utest', self.link_task.outputs)
def exec_test(self):
status = 0
variant = self.env.variant()
filename = self.inputs[0].abspath(self.env)
try:
fu = getattr(self.generator.bld, 'all_test_paths')
except AttributeError:
fu = os.environ.copy()
self.generator.bld.all_test_paths = fu
lst = []
for obj in self.generator.bld.all_task_gen:
link_task = getattr(obj, 'link_task', None)
if link_task and link_task.env.variant() == variant:
lst.append(link_task.outputs[0].parent.abspath(obj.env))
def add_path(dct, path, var):
dct[var] = os.pathsep.join(Utils.to_list(path) + [os.environ.get(var, '')])
if sys.platform == 'win32':
add_path(fu, lst, 'PATH')
elif sys.platform == 'darwin':
add_path(fu, lst, 'DYLD_LIBRARY_PATH')
add_path(fu, lst, 'LD_LIBRARY_PATH')
else:
add_path(fu, lst, 'LD_LIBRARY_PATH')
cwd = getattr(self.generator, 'ut_cwd', '') or self.inputs[0].parent.abspath(self.env)
proc = Utils.pproc.Popen(filename, cwd=cwd, env=fu, stderr=Utils.pproc.PIPE, stdout=Utils.pproc.PIPE)
(stdout, stderr) = proc.communicate()
tup = (filename, proc.returncode, stdout, stderr)
self.generator.utest_result = tup
testlock.acquire()
try:
bld = self.generator.bld
Logs.debug("ut: %r", tup)
try:
bld.utest_results.append(tup)
except AttributeError:
bld.utest_results = [tup]
finally:
testlock.release()
cls = Task.task_type_from_func('utest', func=exec_test, color='PINK', ext_in='.bin')
old = cls.runnable_status
def test_status(self):
if getattr(Options.options, 'all_tests', False):
return RUN_ME
return old(self)
cls.runnable_status = test_status
cls.quiet = 1
def summary(bld):
lst = getattr(bld, 'utest_results', [])
if lst:
Utils.pprint('CYAN', 'execution summary')
total = len(lst)
tfail = len([x for x in lst if x[1]])
Utils.pprint('CYAN', ' tests that pass %d/%d' % (total-tfail, total))
for (f, code, out, err) in lst:
if not code:
Utils.pprint('CYAN', ' %s' % f)
Utils.pprint('CYAN', ' tests that fail %d/%d' % (tfail, total))
for (f, code, out, err) in lst:
if code:
Utils.pprint('CYAN', ' %s' % f)
#!/usr/bin/env python
# encoding: utf-8
# Brant Young, 2007
"This hook is called when the class cpp/cc task generator encounters a '.rc' file: X{.rc -> [.res|.rc.o]}"
import os, sys, re
import TaskGen, Task
from Utils import quote_whitespace
from TaskGen import extension
EXT_WINRC = ['.rc']
winrc_str = '${WINRC} ${_CPPDEFFLAGS} ${_CCDEFFLAGS} ${WINRCFLAGS} ${_CPPINCFLAGS} ${_CCINCFLAGS} ${WINRC_TGT_F} ${TGT} ${WINRC_SRC_F} ${SRC}'
@extension(EXT_WINRC)
def rc_file(self, node):
obj_ext = '.rc.o'
if self.env['WINRC_TGT_F'] == '/fo': obj_ext = '.res'
rctask = self.create_task('winrc', node, node.change_ext(obj_ext))
self.compiled_tasks.append(rctask)
# create our action, for use with rc file
Task.simple_task_type('winrc', winrc_str, color='BLUE', before='cc cxx', shell=False)
def detect(conf):
v = conf.env
winrc = v['WINRC']
v['WINRC_TGT_F'] = '-o'
v['WINRC_SRC_F'] = '-i'
# find rc.exe
if not winrc:
if v['CC_NAME'] in ['gcc', 'cc', 'g++', 'c++']:
winrc = conf.find_program('windres', var='WINRC', path_list = v['PATH'])
elif v['CC_NAME'] == 'msvc':
winrc = conf.find_program('RC', var='WINRC', path_list = v['PATH'])
v['WINRC_TGT_F'] = '/fo'
v['WINRC_SRC_F'] = ''
if not winrc:
conf.fatal('winrc was not found!')
v['WINRCFLAGS'] = ''
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2006-2008 (ita)
# Ralf Habacker, 2006 (rh)
# Yinon Ehrlich, 2009
# Michael Kuhn, 2009
import os, sys
import Configure, Options, Utils
import ccroot, ar
from Configure import conftest
@conftest
def find_xlc(conf):
cc = conf.find_program(['xlc_r', 'xlc'], var='CC', mandatory=True)
cc = conf.cmd_to_list(cc)
conf.env.CC_NAME = 'xlc'
conf.env.CC = cc
@conftest
def find_cpp(conf):
v = conf.env
cpp = None
if v['CPP']: cpp = v['CPP']
elif 'CPP' in conf.environ: cpp = conf.environ['CPP']
if not cpp: cpp = v['CC']
v['CPP'] = cpp
@conftest
def xlc_common_flags(conf):
v = conf.env
# CPPFLAGS CCDEFINES _CCINCFLAGS _CCDEFFLAGS
v['CCFLAGS_DEBUG'] = ['-g']
v['CCFLAGS_RELEASE'] = ['-O2']
v['CC_SRC_F'] = ''
v['CC_TGT_F'] = ['-c', '-o', ''] # shell hack for -MD
v['CPPPATH_ST'] = '-I%s' # template for adding include paths
# linker
if not v['LINK_CC']: v['LINK_CC'] = v['CC']
v['CCLNK_SRC_F'] = ''
v['CCLNK_TGT_F'] = ['-o', ''] # shell hack for -MD
v['LIB_ST'] = '-l%s' # template for adding libs
v['LIBPATH_ST'] = '-L%s' # template for adding libpaths
v['STATICLIB_ST'] = '-l%s'
v['STATICLIBPATH_ST'] = '-L%s'
v['RPATH_ST'] = '-Wl,-rpath,%s'
v['CCDEFINES_ST'] = '-D%s'
v['SONAME_ST'] = ''
v['SHLIB_MARKER'] = ''
v['STATICLIB_MARKER'] = ''
v['FULLSTATIC_MARKER'] = '-static'
# program
v['program_LINKFLAGS'] = ['-Wl,-brtl']
v['program_PATTERN'] = '%s'
# shared library
v['shlib_CCFLAGS'] = ['-fPIC', '-DPIC'] # avoid using -DPIC, -fPIC aleady defines the __PIC__ macro
v['shlib_LINKFLAGS'] = ['-G', '-Wl,-brtl,-bexpfull']
v['shlib_PATTERN'] = 'lib%s.so'
# static lib
v['staticlib_LINKFLAGS'] = ''
v['staticlib_PATTERN'] = 'lib%s.a'
def detect(conf):
conf.find_xlc()
conf.find_cpp()
conf.find_ar()
conf.xlc_common_flags()
conf.cc_load_tools()
conf.cc_add_flags()
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2006 (ita)
# Ralf Habacker, 2006 (rh)
# Yinon Ehrlich, 2009
# Michael Kuhn, 2009
import os, sys
import Configure, Options, Utils
import ccroot, ar
from Configure import conftest
@conftest
def find_xlcxx(conf):
cxx = conf.find_program(['xlc++_r', 'xlc++'], var='CXX', mandatory=True)
cxx = conf.cmd_to_list(cxx)
conf.env.CXX_NAME = 'xlc++'
conf.env.CXX = cxx
@conftest
def find_cpp(conf):
v = conf.env
cpp = None
if v['CPP']: cpp = v['CPP']
elif 'CPP' in conf.environ: cpp = conf.environ['CPP']
if not cpp: cpp = v['CXX']
v['CPP'] = cpp
@conftest
def xlcxx_common_flags(conf):
v = conf.env
# CPPFLAGS CXXDEFINES _CXXINCFLAGS _CXXDEFFLAGS
v['CXXFLAGS_DEBUG'] = ['-g']
v['CXXFLAGS_RELEASE'] = ['-O2']
v['CXX_SRC_F'] = ''
v['CXX_TGT_F'] = ['-c', '-o', ''] # shell hack for -MD
v['CPPPATH_ST'] = '-I%s' # template for adding include paths
# linker
if not v['LINK_CXX']: v['LINK_CXX'] = v['CXX']
v['CXXLNK_SRC_F'] = ''
v['CXXLNK_TGT_F'] = ['-o', ''] # shell hack for -MD
v['LIB_ST'] = '-l%s' # template for adding libs
v['LIBPATH_ST'] = '-L%s' # template for adding libpaths
v['STATICLIB_ST'] = '-l%s'
v['STATICLIBPATH_ST'] = '-L%s'
v['RPATH_ST'] = '-Wl,-rpath,%s'
v['CXXDEFINES_ST'] = '-D%s'
v['SONAME_ST'] = ''
v['SHLIB_MARKER'] = ''
v['STATICLIB_MARKER'] = ''
v['FULLSTATIC_MARKER'] = '-static'
# program
v['program_LINKFLAGS'] = ['-Wl,-brtl']
v['program_PATTERN'] = '%s'
# shared library
v['shlib_CXXFLAGS'] = ['-fPIC', '-DPIC'] # avoid using -DPIC, -fPIC aleady defines the __PIC__ macro
v['shlib_LINKFLAGS'] = ['-G', '-Wl,-brtl,-bexpfull']
v['shlib_PATTERN'] = 'lib%s.so'
# static lib
v['staticlib_LINKFLAGS'] = ''
v['staticlib_PATTERN'] = 'lib%s.a'
def detect(conf):
conf.find_xlcxx()
conf.find_cpp()
conf.find_ar()
conf.xlcxx_common_flags()
conf.cxx_load_tools()
conf.cxx_add_flags()
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2005 (ita)
"""
Utilities, the stable ones are the following:
* h_file: compute a unique value for a file (hash), it uses
the module fnv if it is installed (see waf/utils/fnv & http://code.google.com/p/waf/wiki/FAQ)
else, md5 (see the python docs)
For large projects (projects with more than 15000 files) or slow hard disks and filesystems (HFS)
it is possible to use a hashing based on the path and the size (may give broken cache results)
The method h_file MUST raise an OSError if the file is a folder
import stat
def h_file(filename):
st = os.stat(filename)
if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
m = Utils.md5()
m.update(str(st.st_mtime))
m.update(str(st.st_size))
m.update(filename)
return m.digest()
To replace the function in your project, use something like this:
import Utils
Utils.h_file = h_file
* h_list
* h_fun
* get_term_cols
* ordered_dict
"""
import os, sys, imp, string, errno, traceback, inspect, re, shutil, datetime, gc
# In python 3.0 we can get rid of all this
try: from UserDict import UserDict
except ImportError: from collections import UserDict
if sys.hexversion >= 0x2060000 or os.name == 'java':
import subprocess as pproc
else:
import pproc
import Logs
from Constants import *
try:
from collections import deque
except ImportError:
class deque(list):
def popleft(self):
return self.pop(0)
is_win32 = sys.platform == 'win32'
try:
# defaultdict in python 2.5
from collections import defaultdict as DefaultDict
except ImportError:
class DefaultDict(dict):
def __init__(self, default_factory):
super(DefaultDict, self).__init__()
self.default_factory = default_factory
def __getitem__(self, key):
try:
return super(DefaultDict, self).__getitem__(key)
except KeyError:
value = self.default_factory()
self[key] = value
return value
class WafError(Exception):
def __init__(self, *args):
self.args = args
try:
self.stack = traceback.extract_stack()
except:
pass
Exception.__init__(self, *args)
def __str__(self):
return str(len(self.args) == 1 and self.args[0] or self.args)
class WscriptError(WafError):
def __init__(self, message, wscript_file=None):
if wscript_file:
self.wscript_file = wscript_file
self.wscript_line = None
else:
try:
(self.wscript_file, self.wscript_line) = self.locate_error()
except:
(self.wscript_file, self.wscript_line) = (None, None)
msg_file_line = ''
if self.wscript_file:
msg_file_line = "%s:" % self.wscript_file
if self.wscript_line:
msg_file_line += "%s:" % self.wscript_line
err_message = "%s error: %s" % (msg_file_line, message)
WafError.__init__(self, err_message)
def locate_error(self):
stack = traceback.extract_stack()
stack.reverse()
for frame in stack:
file_name = os.path.basename(frame[0])
is_wscript = (file_name == WSCRIPT_FILE or file_name == WSCRIPT_BUILD_FILE)
if is_wscript:
return (frame[0], frame[1])
return (None, None)
indicator = is_win32 and '\x1b[A\x1b[K%s%s%s\r' or '\x1b[K%s%s%s\r'
try:
from fnv import new as md5
import Constants
Constants.SIG_NIL = 'signofnv'
def h_file(filename):
m = md5()
try:
m.hfile(filename)
x = m.digest()
if x is None: raise OSError("not a file")
return x
except SystemError:
raise OSError("not a file" + filename)
except ImportError:
try:
try:
from hashlib import md5
except ImportError:
from md5 import md5
def h_file(filename):
f = open(filename, 'rb')
m = md5()
while (filename):
filename = f.read(100000)
m.update(filename)
f.close()
return m.digest()
except ImportError:
# portability fixes may be added elsewhere (although, md5 should be everywhere by now)
md5 = None
class ordered_dict(UserDict):
def __init__(self, dict = None):
self.allkeys = []
UserDict.__init__(self, dict)
def __delitem__(self, key):
self.allkeys.remove(key)
UserDict.__delitem__(self, key)
def __setitem__(self, key, item):
if key not in self.allkeys: self.allkeys.append(key)
UserDict.__setitem__(self, key, item)
def exec_command(s, **kw):
if 'log' in kw:
kw['stdout'] = kw['stderr'] = kw['log']
del(kw['log'])
kw['shell'] = isinstance(s, str)
try:
proc = pproc.Popen(s, **kw)
return proc.wait()
except OSError:
return -1
if is_win32:
def exec_command(s, **kw):
if 'log' in kw:
kw['stdout'] = kw['stderr'] = kw['log']
del(kw['log'])
kw['shell'] = isinstance(s, str)
if len(s) > 2000:
startupinfo = pproc.STARTUPINFO()
startupinfo.dwFlags |= pproc.STARTF_USESHOWWINDOW
kw['startupinfo'] = startupinfo
try:
if 'stdout' not in kw:
kw['stdout'] = pproc.PIPE
kw['stderr'] = pproc.PIPE
proc = pproc.Popen(s,**kw)
(stdout, stderr) = proc.communicate()
Logs.info(stdout)
if stderr:
Logs.error(stderr)
return proc.returncode
else:
proc = pproc.Popen(s,**kw)
return proc.wait()
except OSError:
return -1
listdir = os.listdir
if is_win32:
def listdir_win32(s):
if re.match('^[A-Za-z]:$', s):
# os.path.isdir fails if s contains only the drive name... (x:)
s += os.sep
if not os.path.isdir(s):
e = OSError()
e.errno = errno.ENOENT
raise e
return os.listdir(s)
listdir = listdir_win32
def waf_version(mini = 0x010000, maxi = 0x100000):
"Halts if the waf version is wrong"
ver = HEXVERSION
try: min_val = mini + 0
except TypeError: min_val = int(mini.replace('.', '0'), 16)
if min_val > ver:
Logs.error("waf version should be at least %s (%s found)" % (mini, ver))
sys.exit(0)
try: max_val = maxi + 0
except TypeError: max_val = int(maxi.replace('.', '0'), 16)
if max_val < ver:
Logs.error("waf version should be at most %s (%s found)" % (maxi, ver))
sys.exit(0)
def python_24_guard():
if sys.hexversion < 0x20400f0 or sys.hexversion >= 0x3000000:
raise ImportError("Waf requires Python >= 2.3 but the raw source requires Python 2.4, 2.5 or 2.6")
def ex_stack():
exc_type, exc_value, tb = sys.exc_info()
if Logs.verbose > 1:
exc_lines = traceback.format_exception(exc_type, exc_value, tb)
return ''.join(exc_lines)
return str(exc_value)
def to_list(sth):
if isinstance(sth, str):
return sth.split()
else:
return sth
g_loaded_modules = {}
"index modules by absolute path"
g_module=None
"the main module is special"
def load_module(file_path, name=WSCRIPT_FILE):
"this function requires an absolute path"
try:
return g_loaded_modules[file_path]
except KeyError:
pass
module = imp.new_module(name)
try:
code = readf(file_path, m='rU')
except (IOError, OSError):
raise WscriptError('Could not read the file %r' % file_path)
module.waf_hash_val = code
sys.path.insert(0, os.path.dirname(file_path))
try:
exec(compile(code, file_path, 'exec'), module.__dict__)
except Exception:
exc_type, exc_value, tb = sys.exc_info()
raise WscriptError("".join(traceback.format_exception(exc_type, exc_value, tb)), file_path)
sys.path.pop(0)
g_loaded_modules[file_path] = module
return module
def set_main_module(file_path):
"Load custom options, if defined"
global g_module
g_module = load_module(file_path, 'wscript_main')
g_module.root_path = file_path
try:
g_module.APPNAME
except:
g_module.APPNAME = 'noname'
try:
g_module.VERSION
except:
g_module.VERSION = '1.0'
# note: to register the module globally, use the following:
# sys.modules['wscript_main'] = g_module
def to_hashtable(s):
"used for importing env files"
tbl = {}
lst = s.split('\n')
for line in lst:
if not line: continue
mems = line.split('=')
tbl[mems[0]] = mems[1]
return tbl
def get_term_cols():
"console width"
return 80
try:
import struct, fcntl, termios
except ImportError:
pass
else:
if Logs.got_tty:
def myfun():
dummy_lines, cols = struct.unpack("HHHH", \
fcntl.ioctl(sys.stderr.fileno(),termios.TIOCGWINSZ , \
struct.pack("HHHH", 0, 0, 0, 0)))[:2]
return cols
# we actually try the function once to see if it is suitable
try:
myfun()
except:
pass
else:
get_term_cols = myfun
rot_idx = 0
rot_chr = ['\\', '|', '/', '-']
"the rotation character in the progress bar"
def split_path(path):
return path.split('/')
def split_path_cygwin(path):
if path.startswith('//'):
ret = path.split('/')[2:]
ret[0] = '/' + ret[0]
return ret
return path.split('/')
re_sp = re.compile('[/\\\\]')
def split_path_win32(path):
if path.startswith('\\\\'):
ret = re.split(re_sp, path)[2:]
ret[0] = '\\' + ret[0]
return ret
return re.split(re_sp, path)
if sys.platform == 'cygwin':
split_path = split_path_cygwin
elif is_win32:
split_path = split_path_win32
def copy_attrs(orig, dest, names, only_if_set=False):
for a in to_list(names):
u = getattr(orig, a, ())
if u or not only_if_set:
setattr(dest, a, u)
def def_attrs(cls, **kw):
'''
set attributes for class.
@param cls [any class]: the class to update the given attributes in.
@param kw [dictionary]: dictionary of attributes names and values.
if the given class hasn't one (or more) of these attributes, add the attribute with its value to the class.
'''
for k, v in kw.iteritems():
if not hasattr(cls, k):
setattr(cls, k, v)
def quote_define_name(path):
fu = re.compile("[^a-zA-Z0-9]").sub("_", path)
fu = fu.upper()
return fu
def quote_whitespace(path):
return (path.strip().find(' ') > 0 and '"%s"' % path or path).replace('""', '"')
def trimquotes(s):
if not s: return ''
s = s.rstrip()
if s[0] == "'" and s[-1] == "'": return s[1:-1]
return s
def h_list(lst):
m = md5()
m.update(str(lst))
return m.digest()
def h_fun(fun):
try:
return fun.code
except AttributeError:
try:
h = inspect.getsource(fun)
except IOError:
h = "nocode"
try:
fun.code = h
except AttributeError:
pass
return h
def pprint(col, str, label='', sep=os.linesep):
"print messages in color"
sys.stderr.write("%s%s%s %s%s" % (Logs.colors(col), str, Logs.colors.NORMAL, label, sep))
def check_dir(dir):
"""If a folder doesn't exists, create it."""
try:
os.stat(dir)
except OSError:
try:
os.makedirs(dir)
except OSError, e:
raise WafError("Cannot create folder '%s' (original error: %s)" % (dir, e))
def cmd_output(cmd, **kw):
silent = False
if 'silent' in kw:
silent = kw['silent']
del(kw['silent'])
if 'e' in kw:
tmp = kw['e']
del(kw['e'])
kw['env'] = tmp
kw['shell'] = isinstance(cmd, str)
kw['stdout'] = pproc.PIPE
if silent:
kw['stderr'] = pproc.PIPE
try:
p = pproc.Popen(cmd, **kw)
output = p.communicate()[0]
except OSError, e:
raise ValueError(str(e))
if p.returncode:
if not silent:
msg = "command execution failed: %s -> %r" % (cmd, str(output))
raise ValueError(msg)
output = ''
return output
reg_subst = re.compile(r"(\\\\)|(\$\$)|\$\{([^}]+)\}")
def subst_vars(expr, params):
"substitute ${PREFIX}/bin in /usr/local/bin"
def repl_var(m):
if m.group(1):
return '\\'
if m.group(2):
return '$'
try:
# environments may contain lists
return params.get_flat(m.group(3))
except AttributeError:
return params[m.group(3)]
return reg_subst.sub(repl_var, expr)
def unversioned_sys_platform_to_binary_format(unversioned_sys_platform):
"infers the binary format from the unversioned_sys_platform name."
if unversioned_sys_platform in ('linux', 'freebsd', 'netbsd', 'openbsd', 'sunos'):
return 'elf'
elif unversioned_sys_platform == 'darwin':
return 'mac-o'
elif unversioned_sys_platform in ('win32', 'cygwin', 'uwin', 'msys'):
return 'pe'
# TODO we assume all other operating systems are elf, which is not true.
# we may set this to 'unknown' and have ccroot and other tools handle the case "gracefully" (whatever that means).
return 'elf'
def unversioned_sys_platform():
"""returns an unversioned name from sys.platform.
sys.plaform is not very well defined and depends directly on the python source tree.
The version appended to the names is unreliable as it's taken from the build environment at the time python was built,
i.e., it's possible to get freebsd7 on a freebsd8 system.
So we remove the version from the name, except for special cases where the os has a stupid name like os2 or win32.
Some possible values of sys.platform are, amongst others:
aix3 aix4 atheos beos5 darwin freebsd2 freebsd3 freebsd4 freebsd5 freebsd6 freebsd7
generic irix5 irix6 linux2 mac netbsd1 next3 os2emx riscos sunos5 unixware7
Investigating the python source tree may reveal more values.
"""
s = sys.platform
if s == 'java':
# The real OS is hidden under the JVM.
from java.lang import System
s = System.getProperty('os.name')
# see http://lopica.sourceforge.net/os.html for a list of possible values
if s == 'Mac OS X':
return 'darwin'
elif s.startswith('Windows '):
return 'win32'
elif s == 'OS/2':
return 'os2'
elif s == 'HP-UX':
return 'hpux'
elif s in ('SunOS', 'Solaris'):
return 'sunos'
else: s = s.lower()
if s == 'win32' or s.endswith('os2') and s != 'sunos2': return s
return re.split('\d+$', s)[0]
#@deprecated('use unversioned_sys_platform instead')
def detect_platform():
"""this function has been in the Utils module for some time.
It's hard to guess what people have used it for.
It seems its goal is to return an unversionned sys.platform, but it's not handling all platforms.
For example, the version is not removed on freebsd and netbsd, amongst others.
"""
s = sys.platform
# known POSIX
for x in 'cygwin linux irix sunos hpux aix darwin'.split():
# sys.platform may be linux2
if s.find(x) >= 0:
return x
# unknown POSIX
if os.name in 'posix java os2'.split():
return os.name
return s
def load_tool(tool, tooldir=None):
'''
load_tool: import a Python module, optionally using several directories.
@param tool [string]: name of tool to import.
@param tooldir [list]: directories to look for the tool.
@return: the loaded module.
Warning: this function is not thread-safe: plays with sys.path,
so must run in sequence.
'''
if tooldir:
assert isinstance(tooldir, list)
sys.path = tooldir + sys.path
try:
try:
return __import__(tool)
except ImportError, e:
Logs.error('Could not load the tool %r in %r:\n%s' % (tool, sys.path, e))
raise
finally:
if tooldir:
sys.path = sys.path[len(tooldir):]
def readf(fname, m='r'):
"get the contents of a file, it is not used anywhere for the moment"
f = open(fname, m)
try:
txt = f.read()
finally:
f.close()
return txt
def nada(*k, **kw):
"""A function that does nothing"""
pass
def diff_path(top, subdir):
"""difference between two absolute paths"""
top = os.path.normpath(top).replace('\\', '/').split('/')
subdir = os.path.normpath(subdir).replace('\\', '/').split('/')
if len(top) == len(subdir): return ''
diff = subdir[len(top) - len(subdir):]
return os.path.join(*diff)
class Context(object):
"""A base class for commands to be executed from Waf scripts"""
def set_curdir(self, dir):
self.curdir_ = dir
def get_curdir(self):
try:
return self.curdir_
except AttributeError:
self.curdir_ = os.getcwd()
return self.get_curdir()
curdir = property(get_curdir, set_curdir)
def recurse(self, dirs, name=''):
"""The function for calling scripts from folders, it tries to call wscript + function_name
and if that file does not exist, it will call the method 'function_name' from a file named wscript
the dirs can be a list of folders or a string containing space-separated folder paths
"""
if not name:
name = inspect.stack()[1][3]
if isinstance(dirs, str):
dirs = to_list(dirs)
for x in dirs:
if os.path.isabs(x):
nexdir = x
else:
nexdir = os.path.join(self.curdir, x)
base = os.path.join(nexdir, WSCRIPT_FILE)
file_path = base + '_' + name
try:
txt = readf(file_path, m='rU')
except (OSError, IOError):
try:
module = load_module(base)
except OSError:
raise WscriptError('No such script %s' % base)
try:
f = module.__dict__[name]
except KeyError:
raise WscriptError('No function %s defined in %s' % (name, base))
if getattr(self.__class__, 'pre_recurse', None):
self.pre_recurse(f, base, nexdir)
old = self.curdir
self.curdir = nexdir
try:
f(self)
finally:
self.curdir = old
if getattr(self.__class__, 'post_recurse', None):
self.post_recurse(module, base, nexdir)
else:
dc = {'ctx': self}
if getattr(self.__class__, 'pre_recurse', None):
dc = self.pre_recurse(txt, file_path, nexdir)
old = self.curdir
self.curdir = nexdir
try:
try:
exec(compile(txt, file_path, 'exec'), dc)
except Exception:
exc_type, exc_value, tb = sys.exc_info()
raise WscriptError("".join(traceback.format_exception(exc_type, exc_value, tb)), base)
finally:
self.curdir = old
if getattr(self.__class__, 'post_recurse', None):
self.post_recurse(txt, file_path, nexdir)
if is_win32:
old = shutil.copy2
def copy2(src, dst):
old(src, dst)
shutil.copystat(src, src)
setattr(shutil, 'copy2', copy2)
def zip_folder(dir, zip_file_name, prefix):
"""
prefix represents the app to add in the archive
"""
import zipfile
zip = zipfile.ZipFile(zip_file_name, 'w', compression=zipfile.ZIP_DEFLATED)
base = os.path.abspath(dir)
if prefix:
if prefix[-1] != os.sep:
prefix += os.sep
n = len(base)
for root, dirs, files in os.walk(base):
for f in files:
archive_name = prefix + root[n:] + os.sep + f
zip.write(root + os.sep + f, archive_name, zipfile.ZIP_DEFLATED)
zip.close()
def get_elapsed_time(start):
"Format a time delta (datetime.timedelta) using the format DdHhMmS.MSs"
delta = datetime.datetime.now() - start
# cast to int necessary for python 3.0
days = int(delta.days)
hours = int(delta.seconds / 3600)
minutes = int((delta.seconds - hours * 3600) / 60)
seconds = delta.seconds - hours * 3600 - minutes * 60 \
+ float(delta.microseconds) / 1000 / 1000
result = ''
if days:
result += '%dd' % days
if days or hours:
result += '%dh' % hours
if days or hours or minutes:
result += '%dm' % minutes
return '%s%.3fs' % (result, seconds)
if os.name == 'java':
# For Jython (they should really fix the inconsistency)
try:
gc.disable()
gc.enable()
except NotImplementedError:
gc.disable = gc.enable
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2005 (ita)
import sys, os
try:
if (not sys.stderr.isatty()) or (not sys.stdout.isatty()):
raise ValueError('not a tty')
from ctypes import *
class COORD(Structure):
_fields_ = [("X", c_short), ("Y", c_short)]
class SMALL_RECT(Structure):
_fields_ = [("Left", c_short), ("Top", c_short), ("Right", c_short), ("Bottom", c_short)]
class CONSOLE_SCREEN_BUFFER_INFO(Structure):
_fields_ = [("Size", COORD), ("CursorPosition", COORD), ("Attributes", c_short), ("Window", SMALL_RECT), ("MaximumWindowSize", COORD)]
class CONSOLE_CURSOR_INFO(Structure):
_fields_ = [('dwSize',c_ulong), ('bVisible', c_int)]
sbinfo = CONSOLE_SCREEN_BUFFER_INFO()
csinfo = CONSOLE_CURSOR_INFO()
hconsole = windll.kernel32.GetStdHandle(-11)
windll.kernel32.GetConsoleScreenBufferInfo(hconsole, byref(sbinfo))
if sbinfo.Size.X < 10 or sbinfo.Size.Y < 10: raise Exception('small console')
windll.kernel32.GetConsoleCursorInfo(hconsole, byref(csinfo))
except Exception:
pass
else:
import re, threading
to_int = lambda number, default: number and int(number) or default
wlock = threading.Lock()
STD_OUTPUT_HANDLE = -11
STD_ERROR_HANDLE = -12
class AnsiTerm(object):
def __init__(self):
self.hconsole = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
self.cursor_history = []
def screen_buffer_info(self):
sbinfo = CONSOLE_SCREEN_BUFFER_INFO()
windll.kernel32.GetConsoleScreenBufferInfo(self.hconsole, byref(sbinfo))
return sbinfo
def clear_line(self, param):
mode = param and int(param) or 0
sbinfo = self.screen_buffer_info()
if mode == 1: # Clear from begining of line to cursor position
line_start = COORD(0, sbinfo.CursorPosition.Y)
line_length = sbinfo.Size.X
elif mode == 2: # Clear entire line
line_start = COORD(sbinfo.CursorPosition.X, sbinfo.CursorPosition.Y)
line_length = sbinfo.Size.X - sbinfo.CursorPosition.X
else: # Clear from cursor position to end of line
line_start = sbinfo.CursorPosition
line_length = sbinfo.Size.X - sbinfo.CursorPosition.X
chars_written = c_int()
windll.kernel32.FillConsoleOutputCharacterA(self.hconsole, c_char(' '), line_length, line_start, byref(chars_written))
windll.kernel32.FillConsoleOutputAttribute(self.hconsole, sbinfo.Attributes, line_length, line_start, byref(chars_written))
def clear_screen(self, param):
mode = to_int(param, 0)
sbinfo = self.screen_buffer_info()
if mode == 1: # Clear from begining of screen to cursor position
clear_start = COORD(0, 0)
clear_length = sbinfo.CursorPosition.X * sbinfo.CursorPosition.Y
elif mode == 2: # Clear entire screen and return cursor to home
clear_start = COORD(0, 0)
clear_length = sbinfo.Size.X * sbinfo.Size.Y
windll.kernel32.SetConsoleCursorPosition(self.hconsole, clear_start)
else: # Clear from cursor position to end of screen
clear_start = sbinfo.CursorPosition
clear_length = ((sbinfo.Size.X - sbinfo.CursorPosition.X) + sbinfo.Size.X * (sbinfo.Size.Y - sbinfo.CursorPosition.Y))
chars_written = c_int()
windll.kernel32.FillConsoleOutputCharacterA(self.hconsole, c_char(' '), clear_length, clear_start, byref(chars_written))
windll.kernel32.FillConsoleOutputAttribute(self.hconsole, sbinfo.Attributes, clear_length, clear_start, byref(chars_written))
def push_cursor(self, param):
sbinfo = self.screen_buffer_info()
self.cursor_history.push(sbinfo.CursorPosition)
def pop_cursor(self, param):
if self.cursor_history:
old_pos = self.cursor_history.pop()
windll.kernel32.SetConsoleCursorPosition(self.hconsole, old_pos)
def set_cursor(self, param):
x, sep, y = param.partition(';')
x = to_int(x, 1) - 1
y = to_int(y, 1) - 1
sbinfo = self.screen_buffer_info()
new_pos = COORD(
min(max(0, x), sbinfo.Size.X),
min(max(0, y), sbinfo.Size.Y)
)
windll.kernel32.SetConsoleCursorPosition(self.hconsole, new_pos)
def set_column(self, param):
x = to_int(param, 1) - 1
sbinfo = self.screen_buffer_info()
new_pos = COORD(
min(max(0, x), sbinfo.Size.X),
sbinfo.CursorPosition.Y
)
windll.kernel32.SetConsoleCursorPosition(self.hconsole, new_pos)
def move_cursor(self, x_offset=0, y_offset=0):
sbinfo = self.screen_buffer_info()
new_pos = COORD(
min(max(0, sbinfo.CursorPosition.X + x_offset), sbinfo.Size.X),
min(max(0, sbinfo.CursorPosition.Y + y_offset), sbinfo.Size.Y)
)
windll.kernel32.SetConsoleCursorPosition(self.hconsole, new_pos)
def move_up(self, param):
self.move_cursor(y_offset = -to_int(param, 1))
def move_down(self, param):
self.move_cursor(y_offset = to_int(param, 1))
def move_left(self, param):
self.move_cursor(x_offset = -to_int(param, 1))
def move_right(self, param):
self.move_cursor(x_offset = to_int(param, 1))
def next_line(self, param):
sbinfo = self.screen_buffer_info()
self.move_cursor(
x_offset = -sbinfo.CursorPosition.X,
y_offset = to_int(param, 1)
)
def prev_line(self, param):
sbinfo = self.screen_buffer_info()
self.move_cursor(
x_offset = -sbinfo.CursorPosition.X,
y_offset = -to_int(param, 1)
)
escape_to_color = { (0, 30): 0x0, #black
(0, 31): 0x4, #red
(0, 32): 0x2, #green
(0, 33): 0x4+0x2, #dark yellow
(0, 34): 0x1, #blue
(0, 35): 0x1+0x4, #purple
(0, 36): 0x2+0x4, #cyan
(0, 37): 0x1+0x2+0x4, #grey
(1, 30): 0x1+0x2+0x4, #dark gray
(1, 31): 0x4+0x8, #red
(1, 32): 0x2+0x8, #light green
(1, 33): 0x4+0x2+0x8, #yellow
(1, 34): 0x1+0x8, #light blue
(1, 35): 0x1+0x4+0x8, #light purple
(1, 36): 0x1+0x2+0x8, #light cyan
(1, 37): 0x1+0x2+0x4+0x8, #white
}
def set_color(self, param):
intensity, sep, color = param.partition(';')
intensity = to_int(intensity, 0)
color = to_int(color, 0)
if intensity and not color:
color, intensity = intensity, color
attrib = self.escape_to_color.get((intensity, color), 0x7)
windll.kernel32.SetConsoleTextAttribute(self.hconsole, attrib)
def show_cursor(self,param):
csinfo.bVisible = 1
windll.kernel32.SetConsoleCursorInfo(self.hconsole, byref(csinfo))
def hide_cursor(self,param):
csinfo.bVisible = 0
windll.kernel32.SetConsoleCursorInfo(self.hconsole, byref(csinfo))
ansi_command_table = {
'A': move_up,
'B': move_down,
'C': move_right,
'D': move_left,
'E': next_line,
'F': prev_line,
'G': set_column,
'H': set_cursor,
'f': set_cursor,
'J': clear_screen,
'K': clear_line,
'h': show_cursor,
'l': hide_cursor,
'm': set_color,
's': push_cursor,
'u': pop_cursor,
}
# Match either the escape sequence or text not containing escape sequence
ansi_tokans = re.compile('(?:\x1b\[([0-9?;]*)([a-zA-Z])|([^\x1b]+))')
def write(self, text):
wlock.acquire()
for param, cmd, txt in self.ansi_tokans.findall(text):
if cmd:
cmd_func = self.ansi_command_table.get(cmd)
if cmd_func:
cmd_func(self, param)
else:
chars_written = c_int()
if isinstance(txt, unicode):
windll.kernel32.WriteConsoleW(self.hconsole, txt, len(txt), byref(chars_written), None)
else:
windll.kernel32.WriteConsoleA(self.hconsole, txt, len(txt), byref(chars_written), None)
wlock.release()
def flush(self):
pass
def isatty(self):
return True
sys.stderr = sys.stdout = AnsiTerm()
os.environ['TERM'] = 'vt100'
# borrowed from python 2.5.2c1
# Copyright (c) 2003-2005 by Peter Astrand <astrand@lysator.liu.se>
# Licensed to PSF under a Contributor Agreement.
import sys
mswindows = (sys.platform == "win32")
import os
import types
import traceback
import gc
class CalledProcessError(Exception):
def __init__(self, returncode, cmd):
self.returncode = returncode
self.cmd = cmd
def __str__(self):
return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
if mswindows:
import threading
import msvcrt
if 0:
import pywintypes
from win32api import GetStdHandle, STD_INPUT_HANDLE, \
STD_OUTPUT_HANDLE, STD_ERROR_HANDLE
from win32api import GetCurrentProcess, DuplicateHandle, \
GetModuleFileName, GetVersion
from win32con import DUPLICATE_SAME_ACCESS, SW_HIDE
from win32pipe import CreatePipe
from win32process import CreateProcess, STARTUPINFO, \
GetExitCodeProcess, STARTF_USESTDHANDLES, \
STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE
from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0
else:
from _subprocess import *
class STARTUPINFO:
dwFlags = 0
hStdInput = None
hStdOutput = None
hStdError = None
wShowWindow = 0
class pywintypes:
error = IOError
else:
import select
import errno
import fcntl
import pickle
__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "CalledProcessError"]
try:
MAXFD = os.sysconf("SC_OPEN_MAX")
except:
MAXFD = 256
try:
False
except NameError:
False = 0
True = 1
_active = []
def _cleanup():
for inst in _active[:]:
if inst.poll(_deadstate=sys.maxint) >= 0:
try:
_active.remove(inst)
except ValueError:
pass
PIPE = -1
STDOUT = -2
def call(*popenargs, **kwargs):
return Popen(*popenargs, **kwargs).wait()
def check_call(*popenargs, **kwargs):
retcode = call(*popenargs, **kwargs)
cmd = kwargs.get("args")
if cmd is None:
cmd = popenargs[0]
if retcode:
raise CalledProcessError(retcode, cmd)
return retcode
def list2cmdline(seq):
result = []
needquote = False
for arg in seq:
bs_buf = []
if result:
result.append(' ')
needquote = (" " in arg) or ("\t" in arg) or arg == ""
if needquote:
result.append('"')
for c in arg:
if c == '\\':
bs_buf.append(c)
elif c == '"':
result.append('\\' * len(bs_buf)*2)
bs_buf = []
result.append('\\"')
else:
if bs_buf:
result.extend(bs_buf)
bs_buf = []
result.append(c)
if bs_buf:
result.extend(bs_buf)
if needquote:
result.extend(bs_buf)
result.append('"')
return ''.join(result)
class Popen(object):
def __init__(self, args, bufsize=0, executable=None,
stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=False, shell=False,
cwd=None, env=None, universal_newlines=False,
startupinfo=None, creationflags=0):
_cleanup()
self._child_created = False
if not isinstance(bufsize, (int, long)):
raise TypeError("bufsize must be an integer")
if mswindows:
if preexec_fn is not None:
raise ValueError("preexec_fn is not supported on Windows platforms")
if close_fds:
raise ValueError("close_fds is not supported on Windows platforms")
else:
if startupinfo is not None:
raise ValueError("startupinfo is only supported on Windows platforms")
if creationflags != 0:
raise ValueError("creationflags is only supported on Windows platforms")
self.stdin = None
self.stdout = None
self.stderr = None
self.pid = None
self.returncode = None
self.universal_newlines = universal_newlines
(p2cread, p2cwrite,
c2pread, c2pwrite,
errread, errwrite) = self._get_handles(stdin, stdout, stderr)
self._execute_child(args, executable, preexec_fn, close_fds,
cwd, env, universal_newlines,
startupinfo, creationflags, shell,
p2cread, p2cwrite,
c2pread, c2pwrite,
errread, errwrite)
if mswindows:
if stdin is None and p2cwrite is not None:
os.close(p2cwrite)
p2cwrite = None
if stdout is None and c2pread is not None:
os.close(c2pread)
c2pread = None
if stderr is None and errread is not None:
os.close(errread)
errread = None
if p2cwrite:
self.stdin = os.fdopen(p2cwrite, 'wb', bufsize)
if c2pread:
if universal_newlines:
self.stdout = os.fdopen(c2pread, 'rU', bufsize)
else:
self.stdout = os.fdopen(c2pread, 'rb', bufsize)
if errread:
if universal_newlines:
self.stderr = os.fdopen(errread, 'rU', bufsize)
else:
self.stderr = os.fdopen(errread, 'rb', bufsize)
def _translate_newlines(self, data):
data = data.replace("\r\n", "\n")
data = data.replace("\r", "\n")
return data
def __del__(self, sys=sys):
if not self._child_created:
return
self.poll(_deadstate=sys.maxint)
if self.returncode is None and _active is not None:
_active.append(self)
def communicate(self, input=None):
if [self.stdin, self.stdout, self.stderr].count(None) >= 2:
stdout = None
stderr = None
if self.stdin:
if input:
self.stdin.write(input)
self.stdin.close()
elif self.stdout:
stdout = self.stdout.read()
elif self.stderr:
stderr = self.stderr.read()
self.wait()
return (stdout, stderr)
return self._communicate(input)
if mswindows:
def _get_handles(self, stdin, stdout, stderr):
if stdin is None and stdout is None and stderr is None:
return (None, None, None, None, None, None)
p2cread, p2cwrite = None, None
c2pread, c2pwrite = None, None
errread, errwrite = None, None
if stdin is None:
p2cread = GetStdHandle(STD_INPUT_HANDLE)
if p2cread is not None:
pass
elif stdin is None or stdin == PIPE:
p2cread, p2cwrite = CreatePipe(None, 0)
p2cwrite = p2cwrite.Detach()
p2cwrite = msvcrt.open_osfhandle(p2cwrite, 0)
elif isinstance(stdin, int):
p2cread = msvcrt.get_osfhandle(stdin)
else:
p2cread = msvcrt.get_osfhandle(stdin.fileno())
p2cread = self._make_inheritable(p2cread)
if stdout is None:
c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE)
if c2pwrite is not None:
pass
elif stdout is None or stdout == PIPE:
c2pread, c2pwrite = CreatePipe(None, 0)
c2pread = c2pread.Detach()
c2pread = msvcrt.open_osfhandle(c2pread, 0)
elif isinstance(stdout, int):
c2pwrite = msvcrt.get_osfhandle(stdout)
else:
c2pwrite = msvcrt.get_osfhandle(stdout.fileno())
c2pwrite = self._make_inheritable(c2pwrite)
if stderr is None:
errwrite = GetStdHandle(STD_ERROR_HANDLE)
if errwrite is not None:
pass
elif stderr is None or stderr == PIPE:
errread, errwrite = CreatePipe(None, 0)
errread = errread.Detach()
errread = msvcrt.open_osfhandle(errread, 0)
elif stderr == STDOUT:
errwrite = c2pwrite
elif isinstance(stderr, int):
errwrite = msvcrt.get_osfhandle(stderr)
else:
errwrite = msvcrt.get_osfhandle(stderr.fileno())
errwrite = self._make_inheritable(errwrite)
return (p2cread, p2cwrite,
c2pread, c2pwrite,
errread, errwrite)
def _make_inheritable(self, handle):
return DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), 0, 1, DUPLICATE_SAME_ACCESS)
def _find_w9xpopen(self):
w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)), "w9xpopen.exe")
if not os.path.exists(w9xpopen):
w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix), "w9xpopen.exe")
if not os.path.exists(w9xpopen):
raise RuntimeError("Cannot locate w9xpopen.exe, which is needed for Popen to work with your shell or platform.")
return w9xpopen
def _execute_child(self, args, executable, preexec_fn, close_fds,
cwd, env, universal_newlines,
startupinfo, creationflags, shell,
p2cread, p2cwrite,
c2pread, c2pwrite,
errread, errwrite):
if not isinstance(args, types.StringTypes):
args = list2cmdline(args)
if startupinfo is None:
startupinfo = STARTUPINFO()
if None not in (p2cread, c2pwrite, errwrite):
startupinfo.dwFlags |= STARTF_USESTDHANDLES
startupinfo.hStdInput = p2cread
startupinfo.hStdOutput = c2pwrite
startupinfo.hStdError = errwrite
if shell:
startupinfo.dwFlags |= STARTF_USESHOWWINDOW
startupinfo.wShowWindow = SW_HIDE
comspec = os.environ.get("COMSPEC", "cmd.exe")
args = comspec + " /c " + args
if (GetVersion() >= 0x80000000L or
os.path.basename(comspec).lower() == "command.com"):
w9xpopen = self._find_w9xpopen()
args = '"%s" %s' % (w9xpopen, args)
creationflags |= CREATE_NEW_CONSOLE
try:
hp, ht, pid, tid = CreateProcess(executable, args, None, None, 1, creationflags, env, cwd, startupinfo)
except pywintypes.error, e:
raise WindowsError(*e.args)
self._child_created = True
self._handle = hp
self.pid = pid
ht.Close()
if p2cread is not None:
p2cread.Close()
if c2pwrite is not None:
c2pwrite.Close()
if errwrite is not None:
errwrite.Close()
def poll(self, _deadstate=None):
if self.returncode is None:
if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0:
self.returncode = GetExitCodeProcess(self._handle)
return self.returncode
def wait(self):
if self.returncode is None:
obj = WaitForSingleObject(self._handle, INFINITE)
self.returncode = GetExitCodeProcess(self._handle)
return self.returncode
def _readerthread(self, fh, buffer):
buffer.append(fh.read())
def _communicate(self, input):
stdout = None
stderr = None
if self.stdout:
stdout = []
stdout_thread = threading.Thread(target=self._readerthread, args=(self.stdout, stdout))
stdout_thread.setDaemon(True)
stdout_thread.start()
if self.stderr:
stderr = []
stderr_thread = threading.Thread(target=self._readerthread, args=(self.stderr, stderr))
stderr_thread.setDaemon(True)
stderr_thread.start()
if self.stdin:
if input is not None:
self.stdin.write(input)
self.stdin.close()
if self.stdout:
stdout_thread.join()
if self.stderr:
stderr_thread.join()
if stdout is not None:
stdout = stdout[0]
if stderr is not None:
stderr = stderr[0]
if self.universal_newlines and hasattr(file, 'newlines'):
if stdout:
stdout = self._translate_newlines(stdout)
if stderr:
stderr = self._translate_newlines(stderr)
self.wait()
return (stdout, stderr)
else:
def _get_handles(self, stdin, stdout, stderr):
p2cread, p2cwrite = None, None
c2pread, c2pwrite = None, None
errread, errwrite = None, None
if stdin is None:
pass
elif stdin == PIPE:
p2cread, p2cwrite = os.pipe()
elif isinstance(stdin, int):
p2cread = stdin
else:
p2cread = stdin.fileno()
if stdout is None:
pass
elif stdout == PIPE:
c2pread, c2pwrite = os.pipe()
elif isinstance(stdout, int):
c2pwrite = stdout
else:
c2pwrite = stdout.fileno()
if stderr is None:
pass
elif stderr == PIPE:
errread, errwrite = os.pipe()
elif stderr == STDOUT:
errwrite = c2pwrite
elif isinstance(stderr, int):
errwrite = stderr
else:
errwrite = stderr.fileno()
return (p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite)
def _set_cloexec_flag(self, fd):
try:
cloexec_flag = fcntl.FD_CLOEXEC
except AttributeError:
cloexec_flag = 1
old = fcntl.fcntl(fd, fcntl.F_GETFD)
fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag)
def _close_fds(self, but):
for i in xrange(3, MAXFD):
if i == but:
continue
try:
os.close(i)
except:
pass
def _execute_child(self, args, executable, preexec_fn, close_fds,
cwd, env, universal_newlines, startupinfo, creationflags, shell,
p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite):
if isinstance(args, types.StringTypes):
args = [args]
else:
args = list(args)
if shell:
args = ["/bin/sh", "-c"] + args
if executable is None:
executable = args[0]
errpipe_read, errpipe_write = os.pipe()
self._set_cloexec_flag(errpipe_write)
gc_was_enabled = gc.isenabled()
gc.disable()
try:
self.pid = os.fork()
except:
if gc_was_enabled:
gc.enable()
raise
self._child_created = True
if self.pid == 0:
try:
if p2cwrite:
os.close(p2cwrite)
if c2pread:
os.close(c2pread)
if errread:
os.close(errread)
os.close(errpipe_read)
if p2cread:
os.dup2(p2cread, 0)
if c2pwrite:
os.dup2(c2pwrite, 1)
if errwrite:
os.dup2(errwrite, 2)
if p2cread and p2cread not in (0,):
os.close(p2cread)
if c2pwrite and c2pwrite not in (p2cread, 1):
os.close(c2pwrite)
if errwrite and errwrite not in (p2cread, c2pwrite, 2):
os.close(errwrite)
if close_fds:
self._close_fds(but=errpipe_write)
if cwd is not None:
os.chdir(cwd)
if preexec_fn:
apply(preexec_fn)
if env is None:
os.execvp(executable, args)
else:
os.execvpe(executable, args, env)
except:
exc_type, exc_value, tb = sys.exc_info()
exc_lines = traceback.format_exception(exc_type, exc_value, tb)
exc_value.child_traceback = ''.join(exc_lines)
os.write(errpipe_write, pickle.dumps(exc_value))
os._exit(255)
if gc_was_enabled:
gc.enable()
os.close(errpipe_write)
if p2cread and p2cwrite:
os.close(p2cread)
if c2pwrite and c2pread:
os.close(c2pwrite)
if errwrite and errread:
os.close(errwrite)
data = os.read(errpipe_read, 1048576)
os.close(errpipe_read)
if data != "":
os.waitpid(self.pid, 0)
child_exception = pickle.loads(data)
raise child_exception
def _handle_exitstatus(self, sts):
if os.WIFSIGNALED(sts):
self.returncode = -os.WTERMSIG(sts)
elif os.WIFEXITED(sts):
self.returncode = os.WEXITSTATUS(sts)
else:
raise RuntimeError("Unknown child exit status!")
def poll(self, _deadstate=None):
if self.returncode is None:
try:
pid, sts = os.waitpid(self.pid, os.WNOHANG)
if pid == self.pid:
self._handle_exitstatus(sts)
except os.error:
if _deadstate is not None:
self.returncode = _deadstate
return self.returncode
def wait(self):
if self.returncode is None:
pid, sts = os.waitpid(self.pid, 0)
self._handle_exitstatus(sts)
return self.returncode
def _communicate(self, input):
read_set = []
write_set = []
stdout = None
stderr = None
if self.stdin:
self.stdin.flush()
if input:
write_set.append(self.stdin)
else:
self.stdin.close()
if self.stdout:
read_set.append(self.stdout)
stdout = []
if self.stderr:
read_set.append(self.stderr)
stderr = []
input_offset = 0
while read_set or write_set:
rlist, wlist, xlist = select.select(read_set, write_set, [])
if self.stdin in wlist:
bytes_written = os.write(self.stdin.fileno(), buffer(input, input_offset, 512))
input_offset += bytes_written
if input_offset >= len(input):
self.stdin.close()
write_set.remove(self.stdin)
if self.stdout in rlist:
data = os.read(self.stdout.fileno(), 1024)
if data == "":
self.stdout.close()
read_set.remove(self.stdout)
stdout.append(data)
if self.stderr in rlist:
data = os.read(self.stderr.fileno(), 1024)
if data == "":
self.stderr.close()
read_set.remove(self.stderr)
stderr.append(data)
if stdout is not None:
stdout = ''.join(stdout)
if stderr is not None:
stderr = ''.join(stderr)
if self.universal_newlines and hasattr(file, 'newlines'):
if stdout:
stdout = self._translate_newlines(stdout)
if stderr:
stderr = self._translate_newlines(stderr)
self.wait()
return (stdout, stderr)
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2009 (ita)
"""
Fixes for py3k go here
"""
import os
all_modifs = {}
def modif(dir, name, fun):
if name == '*':
lst = []
for y in '. Tools 3rdparty'.split():
for x in os.listdir(os.path.join(dir, y)):
if x.endswith('.py'):
lst.append(y + os.sep + x)
#lst = [y + os.sep + x for x in os.listdir(os.path.join(dir, y)) for y in '. Tools 3rdparty'.split() if x.endswith('.py')]
for x in lst:
modif(dir, x, fun)
return
filename = os.path.join(dir, name)
f = open(filename, 'r')
txt = f.read()
f.close()
txt = fun(txt)
f = open(filename, 'w')
f.write(txt)
f.close()
def subst(filename):
def do_subst(fun):
global all_modifs
try:
all_modifs[filename] += fun
except KeyError:
all_modifs[filename] = [fun]
return fun
return do_subst
@subst('Constants.py')
def r1(code):
code = code.replace("'iluvcuteoverload'", "b'iluvcuteoverload'")
code = code.replace("ABI=7", "ABI=37")
return code
@subst('Tools/ccroot.py')
def r2(code):
code = code.replace("p.stdin.write('\\n')", "p.stdin.write(b'\\n')")
code = code.replace('p.communicate()[0]', 'p.communicate()[0].decode("utf-8")')
return code
@subst('Utils.py')
def r3(code):
code = code.replace("m.update(str(lst))", "m.update(str(lst).encode())")
code = code.replace('p.communicate()[0]', 'p.communicate()[0].decode("utf-8")')
return code
@subst('ansiterm.py')
def r33(code):
code = code.replace('unicode', 'str')
return code
@subst('Task.py')
def r4(code):
code = code.replace("up(self.__class__.__name__)", "up(self.__class__.__name__.encode())")
code = code.replace("up(self.env.variant())", "up(self.env.variant().encode())")
code = code.replace("up(x.parent.abspath())", "up(x.parent.abspath().encode())")
code = code.replace("up(x.name)", "up(x.name.encode())")
code = code.replace('class TaskBase(object):\n\t__metaclass__=store_task_type', 'import binascii\n\nclass TaskBase(object, metaclass=store_task_type):')
code = code.replace('keys=self.cstr_groups.keys()', 'keys=list(self.cstr_groups.keys())')
code = code.replace("sig.encode('hex')", 'binascii.hexlify(sig)')
return code
@subst('Build.py')
def r5(code):
code = code.replace("cPickle.dump(data,file,-1)", "cPickle.dump(data,file)")
code = code.replace('for node in src_dir_node.childs.values():', 'for node in list(src_dir_node.childs.values()):')
return code
@subst('*')
def r6(code):
code = code.replace('xrange', 'range')
code = code.replace('iteritems', 'items')
code = code.replace('maxint', 'maxsize')
code = code.replace('iterkeys', 'keys')
code = code.replace('Error,e:', 'Error as e:')
code = code.replace('Exception,e:', 'Exception as e:')
return code
@subst('TaskGen.py')
def r7(code):
code = code.replace('class task_gen(object):\n\t__metaclass__=register_obj', 'class task_gen(object, metaclass=register_obj):')
return code
@subst('Tools/python.py')
def r8(code):
code = code.replace('proc.communicate()[0]', 'proc.communicate()[0].decode("utf-8")')
return code
@subst('Tools/glib2.py')
def r9(code):
code = code.replace('f.write(c)', 'f.write(c.encode("utf-8"))')
return code
@subst('Tools/config_c.py')
def r10(code):
code = code.replace("key=kw['success']", "key=kw['success']\n\t\t\t\ttry:\n\t\t\t\t\tkey=key.decode('utf-8')\n\t\t\t\texcept:\n\t\t\t\t\tpass")
return code
def fixdir(dir):
global all_modifs
for k in all_modifs:
for v in all_modifs[k]:
modif(os.path.join(dir, 'wafadmin'), k, v)
#print('substitutions finished')
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment