|
|
Documentation for pike-make ver. 2.0
1. Fashion, meet XML Being so popular, and ubiquitous, I put XML support into this make scripts. Sample is provided in the package, I have no reason to doubt your intelligence. 2. Very short intro Still, XML files tend to be verbose beyond measure. That, and tremendous flexibility, are reasons why, in this case, I prefer scripts over XML. Action is what needs to be done, to satisfy some dependency, and is an instance of a CAction. Quite a few methods operate recursively on actions, so it's very convenient thing to declare phony ( empty ) target, which serves just as a container for real targets to be build. If you don't plan on using anything else beyond XML, minimal script would be like this: // get phony action, that is, one which // holds all others, and from where // every action begins CAction phony = GetPhony(); // parse arguments, which, should contain // -X "my_super_duper_project.xml" option, or // scripts in a subfolders meant to build // subprojects CArgs args = CArgs( argc, argv ); // now do actions requested DoArgs( phony, args ); Each action contains environment ( CEnv ), option ( COpts ), file ( CFile ) and dependencies. Environment describes ( include and library ) folders and libraries. Option describes program, its options and various flags. File describes ( source and ) destination folder(s) and file name(s). Dependency is array of other actions. 3. Quick and dirty Basically, you instantiate phony action, then each target you have. Fill those targets with their source files, and finally put those targets as a dependencies of a phony container. Easy:
// declare stuff which repeats
BOOLEAN ifAnsi = TRUE;
string srcDir = "./src/";
string objDir = "./obj/";
string incDirs = "-I./inc/";
string binDir = "./bin/";
string libDir = "./lib/";
string libDirs = "";
string libs = "";
// our phony acquaintance
CAction phony = GetPhony();
// our first target
CAction lib_common = GetTarget( "libcommon", TARGET_SO, ifAnsi, libDir,
libDirs,
libs,
LANG_C );
// add source files to the target
lib_common.AddObjDependency( "cmn_c_types", TARGET_OBJ_SO, ifAnsi, srcDir,
objDir,
incDirs,
LANG_C );
lib_common.AddObjDependency( "cmn_queue", TARGET_OBJ_SO, ifAnsi, srcDir,
objDir,
incDirs,
LANG_C );
// add our target to container
phony.AddDependency( lib_common );
// parse arguments, do the job, in an usual way, i.e.
CArgs args = CArgs( argc, argv );
DoArgs( phony, args );
Target types and languages are defined in a libmake.pmod/libmakedef.pmod as:
// target types
constant TARGET_NONE = 0;
constant TARGET_BIN = 1;
constant TARGET_SO = 2;
constant TARGET_LIB = 3;
constant TARGET_OBJ_BIN = 4;
constant TARGET_OBJ_SO = 5;
constant TARGET_OBJ_LIB = 6;
#define ENUM_TARGET int( 0 .. 6 )
constant LANG_CPP = "C++";
constant LANG_C = "C";
As you can imagine, those target type and language defines defaults used when spawning actions. For instance, if target is library, and language is C++, environment will contain standard C++ library used by gcc ver. 4, i.e. libstdc++. Similarly, when bringing up instance of a COpts, target type and language will determine defaults used for, virtually, everthing ( default action, program called, options passed ). They'll also determine source and destination file extensions. 4. Come get some ( control ) Since environment and options are the same for most of compiling bussiness, why not separate those from common script into its own instances, and then reference those? For instance:
// common environment and options prelude
CEnv envObj = GetObjEnv( incDirs );
COpts optsObjSo = GetCOpts( TARGET_OBJ_SO, LANG_C, ifAnsi );
// assumes folders and such stuff declared as above
CAction lib_common = GetTarget( "libcommon", TARGET_SO, ifAnsi, libDir,
libDirs,
libs,
LANG_C );
lib_common.AddActionDependency( envObj, optsObjSo, "cmn_c_types", srcDir,
objDir,
TARGET_OBJ_SO,
LANG_C );
lib_common.AddActionDependency( envObj, optsObjSo, "cmn_queue", srcDir,
objDir,
TARGET_OBJ_SO,
LANG_C );
phony.AddDependency( lib_common );
I used here convenience functions to bring up instances of CEnv and COpts, of course, you have full-blown create() method for even more control. 5. Explicit actions ;-) Almost everthing is in your own hands, there are only few defaults, most notably, file extensions.
// assumes folders and such, environment, options prologue defined as above
// there can be many actions, files, but we aren't interested in any
// particular instance, so we'll overwrite them each time
CFile f;
CAction a;
f = GetFile( "libcommon", objDir, libDir, TARGET_SO, LANG_C );
CAction lib_common = CAction( envTarget, optsSo, f );
f = GetFile( "cmn_c_types", srcDir, objDir, TARGET_OBJ_SO, LANG_C );
a = CAction( envObj, optsObjSo, f );
lib_common.AddDependency( a );
f = GetFile( "cmn_queue", srcDir, objDir, TARGET_OBJ_SO, LANG_C );
a = CAction( envObj, optsObjSo, f );
lib_common.AddDependency( a );
phony.AddDependency( lib_common );
Again, you could use constructor for each class, to exercise even more control. 6. Ahhhh, much better! As hinted before, you can also add other targets as dependecies to the current one. This would ensure whatever change made to source files of a ( static ) library, will also cause recompilation of a dependant binary. This works well with all methods described above. You can also organize scripts according to subprojects, in their folders, and leave main script, in root folder of a large project, as a minimal 3-liner. I hope you will find it usefull. Send a comment |