Cons FAQ

Version 1.1
21 July 2000
Steven Knight knight@baldmt.com

Subject: 1. Overview

1.1. Introduction

This FAQ contains information about the Cons software construction utility. Much of the information in this FAQ is based on submissions to the Cons mailing list, cons-discuss@eng.fore.com. To join the mailing list, send email to cons-discuss-request@eng.fore.com with the word

      subscribe
      

in the body of the message.

1.2. Current Version of This FAQ

The most recent and up-to-date version of this FAQ may always be found at:

        http://www.baldmt.com/cons-faq/current.html (HTML)
        http://www.baldmt.com/cons-faq/current.txt (text)
      

Please check to make sure your question isn't already answered in the latest version before submitting a new question (or an addition or correction to this FAQ).

1.3. Copyright

This document is copyright 1999-2000 by Steven Knight (knight@baldmt.com). Individual items taken from the Cons mailing list are incorporated with the permission of the credited individuals.

This document may be freely copied, redistributed or modified for any purpose and without fee, provided that this copyright notice is not removed or altered.

Individual items from this document may be excerpted and redistributed without inclusion of the copyright notice.

If you incorporate this FAQ in any commercial, salable, or for-profit collection or product, please be courteous and send a copy to the copyright holder.

1.4. Feedback

Any and all feedback on this FAQ is welcome: corrections to existing answers, suggested new questions, typographical errors, better organization of questions, etc. Contact the author at knight@baldmt.com.

1.5. Credits

Thanks to the following for their contributions to the Cons mailing list and this FAQ:

Ron Aaron, Chris Bartz, Todd J. Derr, Brad M. Garcia, Johan Holmberg, Anthony Kolarik, John Macdonald, Gary Oberbrunner, Jeff Rosenfeld, Jochen Schwarze, Bob Sidebotham, Alex Smith, and Rajesh Vaidheeswarran.


Subject: 2. Table of Contents

  1. Overview

    1.1. Introduction
    1.2. Current Version of This FAQ
    1.3. Copyright
    1.4. Feedback
    1.5. Credits

  2. Table of Contents

  3. General Information

    3.1. What is Cons?
    3.2. Do I need to understand Perl to use Cons?
    3.3. Where do I get Cons?
    3.4. Should I use the stable or development version of Cons?
    3.5. Should I download the Cons base package or development package?
    3.6. Is there a Cons mailing list or newsgroup?
    3.7. Is the mailing list archived anywhere?
    3.8. On what operating systems does Cons run?
    3.9. Who wrote Cons?
    3.10. Who maintains Cons?
    3.11. Who owns the copyright to Cons?
    3.12. How is Cons licensed?

  4. Compatibility with make

    4.1. Is Cons compatible with make?
    4.2. Is there a Makefile-to-Cons or Cons-to-Makefile converter?
    4.3. What are the advantages or disadvantages of Cons vs. Make?
    4.4. How do I do something like 'make install' in Cons?
    4.5. Does Cons support building in parallel, like make's -j option?
    4.6. Does Cons support something like VPATH in make?

  5. Installation Problems

    5.1. Why can't Cons locate a loadable object for module MD5?
    5.2. Why can't Cons locate Config.pm?

  6. Execution Problems

    6.1. Why do I sometimes get an error message when I interrupt Cons?
    6.2. Why didn't Cons build anything when I ran it?
    6.3. Why can't Cons execute my compiler?
    6.4. Why doesn't Cons pass the user's environment to child processes?
    6.5. Why didn't Cons execute its Build directive when I expected?
    6.6. How do I tell Cons that a single command builds multiple targets?
    6.7. How do I use different or multiple compilers?
    6.8. Do I have to put a separate Conscript file in each subdirectory?
    6.9. How do I build multiple software versions from a single source?
    6.10. How can I build or install files above the top-level Construct file?
    6.11. How do I link objects created in separate subdirectories?

  7. Dependencies

    7.1. How does Cons determine build dependencies?
    7.2. Does Cons re-scan a file every time it calculates dependencies?
    7.3. How can I have Cons list the build dependencies?
    7.4. Will Cons use C preprocessor statements to determine dependencies?
    7.5. Shouldn't the platform be part of Cons' dependency analysis?
    7.6. Shouldn't the executable path be part of Cons' dependency analysis?

  8. Language Support

    8.1. Does Cons support C?
    8.2. Does Cons support C++?
    8.3. Does Cons support Java?
    8.4. Does Cons support Fortran?

  9. Files

    9.1. Can I glob filenames in Cons?
    9.2. How does Cons install files?
    9.3. How do I get Cons to install a file only if it doesn't exist at all?
    9.4. Can I replace individual modules in a library?
    9.5. How do I build shared libraries?
    9.6. How can I share files between builds for different architectures?
    9.7. How do I change the suffix for object files?

  10. Windows NT

    10.1. Does Cons recognize Windows NT absolute path names?
    10.2. On Windows NT, how do I specify an absolute path name in CPPPATH?
    10.3. How does Cons deal with case insensitivity in Windows NT?
    10.4. How do I build a DLL using Cons?

  11. Extending Cons

    11.1. How do I add support for new file types (suffixes)?
    11.2. How do I submit a bugfix or a new feature to be added to Cons?

Subject: 3. General Information

3.1. What is Cons?

Cons is a software construction utility--that is, an alternative to "make". It is implemented as a Perl script, which gives it many powerful capabilities not found in other software construction systems.

3.2. Do I need to understand Perl to use Cons?

No. The file formats used by Cons are Perl-like, but do not require specific knowledge of Perl to be useful. Of course, you can use Cons more powerfully if you know Perl well enough to be able to use its features.

3.3. Where do I get Cons?

The Cons home page is:

        http://www.dsmit.com/cons/
      

Mirrors are available at:

        http://www.baldmt.com/cons/
        http://members.home.com/garsh/cons/
      

The page contains information about downloading the latest versions of Cons. Cons is also available on CPAN:

        http://www.CPAN.org/authors/id/K/KN/KNIGHT/
      

3.4. Should I use the stable or development version of Cons?

Following the GNU/Linux numbering convention, Cons is available in stable versions with even minor version numbers (2.0.x, 2.2.x, etc.), and development versions with odd minor version numbers (2.1.x, 2.3.x, etc.).

Development versions of Cons are intended for more frequent release of new features and fixes. Most users should be find using the stable version, unless they want to use a specific feature, or need a specific fix, found in a more-recent development version.

Both stable and development versions of Cons are developed with an extensive regression test suite, so the actual stability of both stable and development versions is generally quite good.

3.5. Should I download the Cons base package or development package?

Each version of Cons can be downloaded in either a base package, which contains Cons and its documentation, or in a developer package, which adds the entire Cons regression test suite. There is no difference between the copies of Cons in a base package or developer package with the same version number.

The developer package is recommended for users who want to run the tests to verify that Cons performs correctly on their system, or who plan to modify or extend Cons and want to make sure their modification doesn't break any existing Cons features. Everyone else shouldn't need anything more than what's in the base package.

3.6. Is there a Cons mailing list or newsgroup?

The Cons mailing list is cons-discuss@gnu.org. To subscribe, send email to cons-discuss-request@gnu.org with the word

      subscribe
      

in the body of the message.

There is no Cons newsgroup on Usenet. (Not yet, anyway.)

3.7. Is the mailing list archived anywhere?

Not as of the writing of this FAQ.

3.8. On what operating systems does Cons run?

Because it is implemented as a Perl script, Cons could conceivably run on any system that supports Perl. Cons is known to be in production use on FreeBSD, Linux, Solaris, SunOS, HPUX, AIX, IRIX, and Windows NT.

3.9. Who wrote Cons?

Cons was originally written by Bob Sidebotham. Bob still participates on the mailing list from time to time, but is not the current maintainer.

3.10. Who maintains Cons?

Cons is currently maintained by Rajesh Vaidheeswarran. Development versions are released by Steven Knight. Questions about Cons should be directed to the cons-dicuss@gnu.org mailing list, however.

3.11. Who owns the copyright to Cons?

Cons is currently copyright the Free Software Foundation. Prior to Cons version 2.0, Cons was copyright Fore Systems, Bob's employer while he was developing Cons.

3.12. How is Cons licensed?

Cons is distributed under the GNU Public License (GPL), so you can use it, modify it, or even redistribute it without charge (so long as you provide source code when you redistribute it). Prior to Cons version 2.0, Fore Systems made Cons available under a license similar to the BSD license.


Subject: 4. Compatibility with make

4.1. Is Cons compatible with make?

No. Cons uses a completely different format for its input files.

4.2. Is there a Makefile-to-Cons or Cons-to-Makefile converter?

No. It would probably be more work than it's worth to write a converter, especially since Cons' approach of building everything from a single process at the top of the directory tree differs radically from the standard recursive use of Make.

4.3. What are the advantages or disadvantages of Cons vs. Make?

Cons' advantages over make are covered in detail in its documentation. The main advantage for many is that Cons has better dependency analysis integrated directly into its build engine, which means that you never have to do the equivalent of a 'make clean' to make sure that everything was rebuilt properly. It's also much faster than Make for typical builds of large directory trees.

The main disadvantage of Cons is that its Construct and Conscript files have some of Perl's syntax-heavy flavor, which can mean a steeper learning curve than some people will tolerate. It also currently lacks a flexible extension mechanism for new file types like Make's suffix rules.

4.4. How do I do something like 'make install' in Cons?

One of the biggest differences between make and Cons is that, in Cons, specified build targets are *only* files or subdirectories in your tree. Cons doesn't have a way to specify a dummy target of "install" in a way that means, "I don't really want you to build anything called install, I just want you to install a bunch of stuff elsewhere in the tree." (This is covered in the Cons documentation under the heading "No ``special'' targets.")

4.5. Does Cons support building in parallel, like make's -j option?

Not currently. Some experimental versions that support parallel builds are in various stages of development. A web page exists with information about these different versions at http://www.baldmt.com/parallel-cons/

.

4.6. Does Cons support something like VPATH in make?

Yes. The Repository feature and -R option provide functionality very similar to VPATH, although without some inconsistencies that make VPATH somewhat difficult to use. See the Cons documentation for details.


Subject: 5. Installation Problems

5.1. Why can't Cons locate a loadable object for module MD5?

After installation, Cons might generate an error message similar to the following:

          $ cons -V
          Can't locate loadable object for module MD5 in @INC (@INC contains: 
          /opt/perl/lib/sun4-solaris/5.00404 /opt/perl/lib 
          /opt/perl/lib/site_perl/sun4-solaris /opt/perl/lib/site_perl .) at cons line XXX
          BEGIN failed--compilation aborted at cons line XXX.
      

This is generally because the Perl MD5 module has not been properly installed; just the MD5.pm file alone is not enough. The "loadable object" Perl wants is a shared library generated by xsubpp, and may be generated by a doing a full installation (perl makefile.PL; make install) of the MD5 module.

5.2. Why can't Cons locate Config.pm?

After installation, Cons might generate an error message similar to the following:

          $ cons -x
          Can't locate Config.pm in @INC at /usr/swlocal/lib/perl5/DynaLoader.pm line 18.
          BEGIN failed--compilation aborted at /usr/swlocal/bin/cons line 1964.
      

This is because the Perl binary you're using does not have the MD5 module installed at all. The MD5 module should be installed as above.

If there are multiple versions of Perl installed on your system, 'which perl' will give the perl binary that is being used.


Subject: 6. Execution Problems

6.1. Why do I sometimes get an error message when I interrupt Cons?

Q:     

Why, when I kill Cons with ^C, does it print the following msg before exiting?

                cons: error in file "docs-spark/Conscript" (Undefined subroutine &sig::hash::END called at ./cons line XXX.)
        
[Gary Oberbrunner , 16 April 1998]
A:     

Perl. This comes up on p5p every once in a while.

As I understand it, the problem is this: a signal can arrive between any two instructions, some signals must be at least partially dealt with at once. If perl was in the middle of an action such as malloc, it might not be safe to interrupt in the middle and then evaluate more perl code before returning to that original position. There are ideas floating around for dealing with it and lots of desire to have it dealt with, but it is not easy (not completely and correctly anyhow).

[John Macdonald , 16 April 1998]

6.2. Why didn't Cons build anything when I ran it?

A:     

You have to give it a target, usually a directory. Try

                perl cons.pl .
        

(that's a dot at the end of the line).

[Gary Oberbrunner , 12 May 1998]
A:     

Alternatively, you can use the 'Default' function to specify one or more default targets to build when no targets are specified on the command line. The following in a Construct file:

                Default qw(
                        .
                );
        

will mimic make's behavior of building the world by default.

6.3. Why can't Cons execute my compiler?

Q:     

I tried to run the 'hello' example. My Construct is:

            $env = new cons(
                CC => 'gcc',
            );

            Program $env 'hello', 'hello.c'
        

When I type 'cons hello', I get this output:

            gcc -c hello.c -o hello.o
            gcc: installation problem, cannot exec `cpp': Invalid argument
            cons: *** [hello.o] Error 256
            cons: errors constructing hello.o
        

Which is strange, since I can *manually* type:

            gcc -c hello.c hello.o
        

and it works just fine. I am guessing my PATH or GCC_EXEC_PREFIX is getting munged by 'cons', because :

            perl -e "system 'gcc -c hello.c hello.o'"
        

works just fine too!!!

[Ron Aaron , 13 May 1999]
A:     

Cons does not pass the user's environment to the child processes that it forks to build the software. Anything you need or want to pass in from the user's environment must be done so explicitly. In practice, this isn't hard at all:

            $Env = new cons(
                ENV => {
                    PATH => $ENV{PATH} . ":/bin:/usr/bin",
                    USER => $ENV{USER},
                }
            );
        

Create any other, subsidiary Cons environments using $Env->clone, and everything else in your build gets the PATH that you want.

[Steven Knight , 13 May 1999]

6.4. Why doesn't Cons pass the user's environment to child processes?

A:     

This is for purposes of repeatability, so the build won't work for me because I have some weird thing in my path (or some other weird env variable that might alter compiler behavior) and yet fail for everyone else. Cons wants you to explicity set the path to what you need it to be.

Most folks use Make this way too, for the same reason.

[Gary Oberbrunner , 13 May 1999]

6.5. Why didn't Cons execute its Build directive when I expected?

Q:     

Yet another mechanism that I wanted to implement in CONS but failed was a build script for templates coded in different languages (locales). I have this directory structure:

templates/ templates/en templates/fr

where templates/ are my locale-independent templates and templates/Conscript says 'for each locale, build a locale-specific template' (but do not Install it). The individual locale's Conscript contains the Install directive.

templates/Conscript:

            @Locales = ('en','fr');
            @Sources = ('index.html');
            foreach $Locale (@Locales) {
             Command $CONS qq($Locale), join(' ',@Sources), q(localize %<);
             Build qq($Locale/Conscript);
            }
        

templates/en/Conscript:

            foreach $Source(<*.html>) {
             Install $CONS qq($BIN), qq($Source); 
            }
        

templates/en/Conscript: I thought that CONS will first look at templates/Conscript, run the above Command which will generate a set of files in templates/$Locale, and then Build templates/$Locale/Conscript which will Install the generated files into $BIN. This didn't work. Is there a correct/better way to accomplish this?

[Alex Smith , 26 March 1999]
A:     

Cons directives (Install, Build, Command) can be moderately counter-intuitive, because they're not "executed" at the time they're processed by perl. ("Build" is especially problematic, because it doesn't actually *perform* a build of anything, it just reads up the specified subsidiary Conscript files containing build instructions...)

When the Cons directives are processed, they just set up the appropriate dependency tree and storing the proper build commands for all the targets in all the Construct+Conscript files. It's not until Cons is actually trying to build the targets specified on its command line (or in the Default method) that it does any "real work" of building or installing files.

Fixing your example to generate an index.html file from an index.in file for each locale:

templates/Conscript:

            @Locales = qw(en fr);
            foreach $Locale (@Locales) {
                Build qq($Locale/Conscript);
            }

        templates/en/Conscript:

            @Inputs = qw(index.in);
            foreach $Source (@Inputs) {
                ($target = $Source) =~ s/\.in$/.html/; # index.in => index.html
                Command $CONS qq($target), qq($Source), q(localize %<);
                Install $CONS qq($BIN), qq($target);
            }
        
[Steven Knight , 27 March 1999]

6.6. How do I tell Cons that a single command builds multiple targets?

A:     

Make the target of the Command be a reference to an array containing a list of the targets being built. This is most commonly done by enclosing the list in [square brackets].

In order, for a common example, to build target foo.class and bar.class files from a single invocation of the Java compiler on the input foo.java and bar.java files:

            @sources = qw(foo.java bar.java);
            @targets = qw(foo.class bar.class);
            Command $CONS [@targets], @sources, q(javac %<);
        

As of version 1.8, only the Command directive can take a reference to an array in this way.

A:     

Or, if you have more targets produced from a given set of sources... say, foo.java and bar.java producing foo.class bar.class and baz.class... then this would work for you...

            @sources = qw(foo bar);
            @targets = qw(foo bar baz);

            CompileJava $CONS [@targets], [@sources];

            sub cons::CompileJava {
               my($cons, $tgt, $src) = @_;
               $cons->Command([map(("$_.class"), @$tgt)], map(("$_.java"), @$src),
                   q(javac %< ));
            }
        
[Rajesh Vaidheeswarran , 29 March 1999]

6.7. How do I use different or multiple compilers?

A:     

For C, you need a different environment for each specifying what CC is:

            # C build environment
            $CONS = new cons();

            $CONS_C = clone $CONS(
                    CC               => "gcc",
                    CPPPATH          => $my_cpppath,
            );

            # C++ build environment
            $CONS_CPP = clone $CONS(
                    CC               => "gcc -x c++",
                    CPPPATH          => $my_cplusplus_cpppath,
            );

            @c_sources = qw(a.c b.c c.c);

            @cpp_sources = qw(a.cc b.cc c.cc);

            Library $CONS_C "mylib.a", @c_sources;
            Library $CONS_CPP "mylib.a", @cpp_sources;
        
[Rajesh Vaidheeswarran , 29 March 1999]

6.8. Do I have to put a separate Conscript file in each subdirectory?

A:     

No. You could put all of the information about your entire directory tree in one top-level Construct file that uses relative path names for all file references. In practice, though, most people find it a lot more convenient to list the source files and build rules for a given subdirectory's constructions in that subdirectory. Your mileage may vary.

[Steven Knight , 7 April 1999]

6.9. How do I build multiple software versions from a single source?

Q:     

During one call to cons, we would like to build two versions of a single set of source. We need to direct one source directory to two destinations, since we have to have different object files. We found it a real hassle having to update two sets of very similar sources in separate directories and decided to fold them together and use preprocessor directives to control the compilation.

I didn't find a simple way to do that. Instead, I just created a second set of source files (.cpp) in the same directory as the first, with different names. These source files simply #include their counterpart.

Any cleaner solutions?

[Anthony Kolarik , 3 May 1999]
A:     

Well, in one of the projects at FORE, we build an embedded OS and the boot monitor (bootrom) mostly from the same set of sources.

In another project, we build the exact same sources for a variety of platforms like sunos5, irix6, and other flavors of unix.

In the first case, we need to build both as cross-compiled targets on the same host platform.

In the second case, we will always compile the sources only for the target platform on itself.

The second case is easier compared to the first, so I'll give you an idea of how we approached the first one.

I can't cut and paste the Conscripts here due to the copyrights in them, but I can given you an idea of what you can do.

           # Trees
           $src = "#src";
           $build = "#build";

           $variant1 = "$build/var1";
           $variant2 = "$build/var2";

           Link $build => $src;

           # cons object for variant1
           $cons1 = new cons(CFLAGS => '-DVARIANT1');

           # cons object for variant2
           $cons2 = new cons(CFLAGS => '-DVARIANT2');

           # common sources
           @srcs = qw(a.c b.c c.c)

           # variant1 sources
           @var1src = qw(d.c)

           # variant2 sources
           @var2src = qw(e.c)

           # First Variant of Program `prog' 

           Install $cons1 $variant1, map("$src/$_", @srcs, @var1src);
           Program $cons1 "$variant1/prog", map("$variant1/$_", @srcs, @var1src);

           # Second Variant of Program `prog' 

           Install $cons2 $variant2, map("$src/$_", @srcs, @var2src);
           Program $cons2 "$variant2/prog", map("$variant2/$_", @srcs, @var2src);
        

So, you'll find the final program in variant1 style in build/variant1/prog and in variant2 style in build/variant2/prog

The second case is much easier since you need to just change your build tree to be dependent on the OS.

             chomp ($ostype = `uname`);

             $build = "#build/$ostype";

             Link $build => $src;
        
[Rajesh Vaidheeswarran , 3 May 1999]
A:     

The Link directive can do exactly this when used with multiple build environments. One way is to define multiple Cons environments with different CFLAGS, and then Link+Build the subsidiary program with each environment in turn:

            $ cat Construct
            my $build1 = new cons ( CFLAGS  => '-DVERSION=1' );

            my $build2 = new cons ( CFLAGS  => '-DVERSION=2' );

            $Env = $build1;
            Export qw( Env );
            Link 'build1' => 'src';
            Build 'build1/Conscript';

            $Env = $build2;
            Export qw( Env );
            Link 'build2' => 'src';
            Build 'build2/Conscript';
            $
        

You need to assign each build environment to $Env in turn because that's the environment that the subsidiary 'src' directory's Conscript file is expecting to Import:

            $ cat src/Conscript
            # src/Conscript

            Import qw( Env );

            Program  $Env 'foo', 'foo.c';
            $
        

Because the src directory is Linked twice, once each with the separate build environments, it gets "called" with a different -DVERSION option in each subdirectory. Then, the common .c file uses normal #if/#ifdef to figure out which VERSION is needed:

            $ cat src/foo.c
            /*
             * src/foo.c
             */

            main()
            {
            #if VERSION == 1
                    printf("This is version 1\n");
            #elif VERSION == 2
                    printf("This is version 2\n");
            #else
            #error Must -DVERSION=1 or -DVERSION=2 when compiling!
            #endif
            }
        

Put it all together, and the build looks like this:

            $ cons . 
            cc -DVERSION=1 -c build1/foo.c -o build1/foo.o
            cc -o build1/foo build1/foo.o
            cc -DVERSION=2 -c build2/foo.c -o build2/foo.o
            cc -o build2/foo build2/foo.o
            $ build1/foo
            This is version 1
            $ build2/foo
            This is version 2
            $ 
        

Another variation on this theme would have separate build1/Conscript and build2/Conscript files, each defining their own environment and Linking to the top-level src directory. This is easier to manage if there is a large number of different build environment (or if the environments themselves are complicated).

[Steven Knight , 3 May 1999]

6.10. How can I build or install files above the top-level Construct file?

A:     

Upgrade to Cons 2.0 and use "../" to ascend the directory tree as you'd expect. (Prior to version 2.0, Cons didn't know how to build things above its top-level directory without some messy workarounds.)

6.11. How do I link objects created in separate subdirectories?

Q:     

If I build object files spread out in multiple directories, each generated by a separate Conscript file, how can I link them all together into another relocatable module?

A:     

In each Conscript file, include a LinkedModule directive, similar to:

            LinkedModule $ENV "#lib/combined.o", qw (
                source1.c
                source2.c
                object1.o
            );
        

The list of files will be different in each Conscript file, but cons will basically combine all of these lists together to create the final lib/combined.o file.

[Brad M. Garcia , 19 August 1999]

Subject: 7. Dependencies

7.1. How does Cons determine build dependencies?

Unlike Make, which requires that dependencies be listed explicitly in the Makefiles, Cons scans source files directly to determine the dependent files. This requires that Cons understand the dependency rules for a given language that it is building. For C and C++, Cons has efficient built-in scanning rules that use Perl's regular expressions to search for #include lines. There is no native Cons support for other languages, but Cons offers a QuickScan feature that allows you to write a new dependency-scanning function and associated it with source files that require it. Consult the documentation for details on how to use QuickScan.

7.2. Does Cons re-scan a file every time it calculates dependencies?

Not within a single invocation. If multiple source files all #include a given .h file, then Cons only scans that .h file (and any subsidiary .h files that it includes) once, and remembers the result for later files that also #include that .h file.

Every time Cons is invoked, however, it does re-scan each .h file once.

7.3. How can I have Cons list the build dependencies?

The -d option does this. (Note that -d performs a build at the same time; there isn't currently a way to only list dependencies without building.)

7.4. Will Cons use C preprocessor statements to determine dependencies?

No. Any change to a file, regardless of whether it's surrounded by a #ifdef, will change the file's MD5 signature and cause a rebuild of all targets that depend on that file. So cons may believe that a file depends upon more than it actually does. This shouldn't hurt anything. If cons can't actually find one of the files that it believes should be a dependency, it simply ignores it.

7.5. Shouldn't the platform be part of Cons' dependency analysis?

Q:     

I'm trying to use Cons to build programs on several different platforms (Solaris, HP-UX, Linux, NT at least).

I was somewhat surprised that that the following could happen:

            hpux% cons hello
            cc -c hello.c -o hello.o
            cc -o hello hello.o
            hpux%
            hpux% rlogin solaris
            solaris%
            solaris% cons hello
            cons: "hello" is up-to-date.
        

Shouldn't the "platform" be part of the signatures in some way?

[Johan Holmberg , 16 August 1999]
A:     

I would prefer that it isn't. We do cross-compiles for the same target machine on several different host machine architectures. Having the platform NOT be part of the signature means we can start a build under Solaris and re-compile just the changes on a Linux box without rebuilding the whole system.

[Brad M. Garcia , 16 August 1999]
Q:     

Is there some simple way I can accomplish this myself?

[Johan Holmberg , 16 August 1999]
A:     

You can accomplish this yourself by adding a "Salt" statement to your Construct file, similar to the following:

            $host_type = `uname -s`;
            Salt($host_type);
        

You can use whatever you want to use to distinguish your build platforms as an argument to Salt.

[Brad M. Garcia , 16 August 1999]

7.6. Shouldn't the executable path be part of Cons' dependency analysis?

Q:     

I'm trying to use Cons to build programs on several different platforms (Solaris, HP-UX, Linux, NT at least).

I was somewhat surprised that that the following could happen:

On HPUX I have two diffeent compilers named "cc", one in /usr/bin and one in /usr/ccs/bin. If I change my Construct file to

            %env = new cons()->copy();
            $env{CC} = "ccc";
            $env{ENV}{PATH} = "/usr/local/bin:/bin:/usr/bin";
            $env = new cons(%env);
        

then build hello, and then later change Construct to

            %env = new cons()->copy();
            $env{CC} = "ccc";
            $env{ENV}{PATH} = "/usr/ccs/bin:/usr/local/bin:/bin:/usr/bin";
            $env = new cons(%env);
        

then cons still thinks hello is up-to-date.

Shouldn't the actual path to "cc" used in the command be part of the signature, insead of just "cc" as I suspect it is now?

[Johan Holmberg , 16 August 1999]
A:     

Perhaps. The dependency analysis for ".o" files could be modified so that it also depends upon the compiler executable file as well as the source files. I would rather have it implemented in this manner, rather than simply going by the file path.

For now, you could simply use $env{ENV}{PATH} as part of the argument to the Salt command.

[Brad M. Garcia , 16 August 1999]

Subject: 8. Language Support

8.1. Does Cons support C?

Yes. Cons was originally written to do a better job than Make at compiling C projects, largely by integrating C dependency analysis into the build tool.

8.2. Does Cons support C++?

Yes. Cons build environments have a SUFMAP by which Cons understands that C++ file suffixes (.cc, .cxx, .cpp) can be built using its internal C compiler object. The suffixes are part of the default environment, so you don't need to modify the SUFMAP to get C++ support.

8.3. Does Cons support Java?

Using Cons to build Java projects is possible, but not straightforward. Java has a number of characteristics that don't fit well with the Cons (and Make) model of creating a dependency tree to determine what needs rebuilding:

        --  Java dependency analysis is more complex than the simple
            #include scanning of C and C++, arising from the classes
            used by the code in addition to the import statements.
        --  As a result of the class dependencies present, the Java
            compiler may create or update multiple output class files
            and directories.
        --  Java may automatically rebuild class files, even if not
            explicitly given as an argument to the compiler.
        --  Java may read possibly obsolete class files from previous
            compiler runs.
      

From the point of view of an outside build tool, the Java compiler is essentially unpredictable, creating or updating whatever files it decides need it. This makes it extremely difficult for a build tool such as Cons to decide what needs updating, or has been updated, by Java.

One approach that has been suggested would extend cons to scan the output of the Java -depend -verbose command to build dependency lists. Some additional mechanism would need to be invented to store Cons signatures on classes that are stored in archives (jar or zip files). This solution does not exist today.

An alternative approach that is already in use by Jochen Schwarze (Jochen.Schwarze@orthogon.de) is to use an intermediate script that passes a list of Java files to the javac compiler, and puts the output class files in a new temporary directory. The script then calls jar to pack the newly-generated class files into a single jar file, essentially sidestepping the dependency issue. A second script is then used to merge the multiple jar files generated by the first script into a single file. This approach fits Cons more readily because the first script acts somewhat like a meta-compiler, creating a single object from Java source files, and the second acts like a linker, creating a single executable from multiple "objects."

The advantage to this approach is that it makes the builds very reliable, because there's no chance that an obsolete but unremoved class file will be incorporated into a build by the javac compiler. The disadvantage, however, is that there's no way to incrementally update a portion of a jar file, since from Cons' point of view, each is created atomically by a single call to the "compile" script.

Jochen has offered in the past to make his scripts available, but he is very busy, so be patient and courteous if you ask him for them.

8.4. Does Cons support Fortran?

Not natively. As distributed, Cons can only parse C/C++ files for include dependencies. You would need to use QuickScan to add Fortran-scanning capability to your own Construct/Conscript files.


Subject: 9. Files

9.1. Can I glob filenames in Cons?

In general, globbing file names isn't recommended because it makes the build dependent on the local environment and potentially non-reproducible. This causes problems, especially for large projects: developer A's build will succeed because his build globs a local file that he to forgot checkin, then it fails for everyone else. Explicitly listing all source files avoids these sorts of problems.

That aside, Cons does not chdir to the directory in which the Conscript file resides, so you can't (as of 2.0.1) use the obvious Perl file-globbing syntax to generate a list of file names:

	  Library $env 'libfoo.a', <*.c>;	# this doesn't work
	

(There was some lengthy mailing list discussion of this two years ago. In that discussion, Bob Sidebotham mentioned that it wouldn't be hard to have Cons chdir to the Conscript directory, but no one's implemented that yet.)

In the meantime, you can work around this by hand, surrounding any globbing code in a Conscript file as follows:

	  use Cwd;
	  $save_cwd = cwd;
	  chdir(DirPath('.'));
	  # GLOBBING CODE
	  chdir($save_cwd);
        

(Modulo proper error checking and robustness...)

9.2. How does Cons install files?

Cons will try to create a hard link to the file first. If that fails (because the installation directory is on a separate partition or physical drive), then Cons will copy the file.

9.3. How do I get Cons to install a file only if it doesn't exist at all?

Q:     

I have the following command:

            Command $env ("$BIN/license.txt", "license.txt",
                          "test -e %> || cp %< %>");
        

The intended result is "replace this file only if it doesn't exist at all in $BIN." But of course it doesn't work, because cons deletes the target before it runs my command, so it always does the copy. Bummer.

Is there a way, short of a fake target that always runs, to do this?

(BTW, what I *really* want is for it only to do the cp if %< is newer than %>, any idea how to do that? Anyway, the existence test is good enough for my application.)

[Gary Oberbrunner , 6 October 1998]
A:     

Assuming that license.txt is not a derived file, then would this do?

            system("cp license.txt $BIN") unless -e "$BIN/license.txt";
        

If license.txt is derived, then this won't do, since it will be done too soon (before the derivation of the file).

[Bob Sidebotham , 6 October 1998]

9.4. Can I replace individual modules in a library?

Q:     

I'm looking for a way to replace files in an existing library using cons. i.e. I have some library 'libfoo.a' containing 'bar.o' and 'baz.o'. I want to build my own version of 'bar.o' from 'bar.c' and then build a new 'libfoo.a' with the new 'bar.o' and the old 'baz.o'.

Is there any easy way to accomplish this? The only way I've come up with so far (I haven't tried it, and it will be a hack regardless) is to extract all of the objects from the existing library and then repackage the whole thing, because cons wants to delete the existing library before it rebuilds it rather than just replacing the files.

[Todd J. Derr , 29 October 1998]
A:     

On most systems (Solaris, Irix at least) it is faster (sometimes by a lot) to rebuild the archive from the .os than to replace ones in the middle.

This is a problem in general though with cons, in that it always deletes the target before running the command even if that's not what you'd like.

[Gary Oberbrunner , 29 October 1998]
A:     

To answer my own question... Does anyone see any problems with this approach?

            Command $CONS "libfoo.a", (
                    "/path/to/original/libfoo.a",
                    Objects $CONS "bar.c",
                ),  
                "cp %1 %>; %AR r %> %<; %RANLIB %>";
        

It seems to be doing exactly what I want it to do. :) a little messy maybe, but less so than trying to extract and re-archive everything.

[Todd J. Derr , 29 October 1998]
A:     

This is definitely the best approach, IMO. What this does is explicitly recognize that you have a source (the original archive) and a separate target. If you try to make the source and the target the same thing, the Cons model doesn't work. For example, if we added an option to Cons to tell it to not remove files, you still have a circular dependency: the signature of the derived file depends on the files that go into it. But in this case, your derived file depends upon the derived file itself. So it would force a re-derivation the next time...

[Bob Sidebotham , 29 October 1998]

9.5. How do I build shared libraries?

Q:     

The Library method is hard-wired to generate non-shared libraries (.a files). How can I build shared libraries (.sl files)?

A:     

As a quick-and-dirty approach, you can use the Program method to build a shared library directly by setting appropriate values for CFLAGS, LINK and LDFLAGS:

            $slenv = $env->clone(CFLAGS => '-fPIC',
                                 LINK => 'gcc',
                                 LDFLAGS => '-shared'); 

            Program $slenv "mydll.sl" "file1.o", "file2.o", ...;
        
A:     

Try adding the function below to your Construct. Use just like Library. You must do the following to use it:

        - Set LINK,LDFLAGS, etc. in your slenv environment to include
          '-shared' as above.
        - Set SUFSHLIB to '.sl'
        - Set SUFLIBS to '.sl:.a' so the shared lib will be found as a
          dependency by your program.  This will solve the problem you are
          having, assuming you are using -lmydll to link with your lib.

        ########################################################################
        # SharedLibrary
        ########################################################################
        # Usage: just like Library, but builds a shared lib.  User must set
        # CFLAGS,LINK,LDFLAGS etc. to proper options for building a shared lib.
        sub cons::SharedLibrary {
            my($env) = shift;
            my($lib) = $dir::cwd->lookup(file::addsuffix(shift, $env->{SUFSHLIB}));
            my($libenv) = $env->_resolve($lib);
            $lib->bind(find build::command::link($libenv, $libenv->{LINKCOM}),
                       $env->_Objects(map($dir::cwd->lookup($_), @_)));
        }
        
[Gary Oberbrunner , 5 March 1999]

9.6. How can I share files between builds for different architectures?

Q:     

I try to set up a build environment where several architectures are built in parallel, but certain targets (for example C files generated by yacc) should be shared between all architectures.

When I try to do this, however, cons always rebuilds the shared targets. I found the reason is that cons includes the build command's time stamp into the target file signature, i.e. if you have a target like

            Command $CONS "y.tab.c", "a.y", "yacc a.y";
        

The signature of y.tab.c contains the command name "yacc" (without path) and the modification time of /usr/bin/yacc, /usr/ccs/bin/yacc or wherever yacc resides. In general, this is probably an advantage, because it forces a rebuild whenever new compiler versions are installed.

Any ideas how to address this problem? One could wrap a simple perl or shell script around yacc. With the script belonging to the source code, the modification time of the script would be the same for all archi- tectures. But this is not exceptionally elegant ...

[Jochen Schwarze , 8 May 1998]
A:     
            Ignore "/yacc\$";
        

But if you ever upgrade yacc, y.tab.c will not get rebuilt automatically.

You might be better off by simply rebuilding y.tab.c for each platform, rather than trying to share it among all platforms.

Another possibility is to create "fake" .consign files in the directories where yacc lives, and give every copy of yacc the exact same checksum.

[Brad M. Garcia , 8 May 1998]

9.7. How do I change the suffix for object files?

Q:     

I'm working on a Construct file which I am trying to set up for multiple compilers and environments.

One of the compilers assumes that objects have a '.obj' ending, not '.o', and it tries to compile '.o' files. Needless to say, this is undesirable :-)

Is there some way to inform cons that the default object extension should be obj?

[Ron Aaron , 13 September 1999]
A:     

Within the cons environment, re-define SUFOBJ:

            $ENV = new cons(
                ...
                SUFOBJ       => '.o',
                ...
            );
        
[Brad M. Garcia , 13 September 1999]

Subject: 10. Windows NT

10.1. Does Cons recognize Windows NT absolute path names?

A:     

Yes, as of Cons 2.0, Windows NT absolute path names can be specified either with volume letters (c:\msvc42\include) or as a UNC path (\\share\include).

(Prior to version 2.0, Cons required a counter-intuitive work-around of beginning volume-letter absolute path names with a '#' to avoid some internal processing.)

10.2. On Windows NT, how do I specify an absolute path name in CPPPATH?

Q:     

How can you specify an absolute path name in NT in CPPPATH. I want CPPPATH to have something like v:\msvc42\include in it.

I can't put the colon in there because that is the delimiter that CONS uses in the CPPPATH. But if I don't put it in there then CONS does something like

                cl /I\v\msvc42\include
        

which doesn't work either.

[Chris Bartz , 29 September 1998]
A:     

In Cons version 1.4a2, you can make CPPPATH be an array reference:

                CPPPATH => [ "#v:\msvc42\include", "another directory", ... ]
        

This is now the preferred way to specify a path; the old way is deprecated.

(BTW, there's a bug (or feature, if you will), currently, in that you have to specify files with drive letters as top-relative names, by starting them with a "#").

[Bob Sidebotham , 29 September 1998]

10.3. How does Cons deal with case insensitivity in Windows NT?

Q:     

I have a problem in NT because of the odd case-insensitivity of NT. If I have one .c file that '#includes "abc.h"' and another that '#include "ABC.H"', they are really including the same file (assuming the same directory or include path, etc.). NT remembers the case that was used to create the file so the filename might be "abc.H", or "Abc.h", whatever, but the name is case insensitive because no matter what case you use to open the file, it will work.

But cons treats them as different. The real problem is where "abc.h" is generated or "Installed" into the location where the compiler will find it. The file is not created in time and/or it is deleted (i.e. unlinked from the Link directory).

When scanning .c files for #includes, if I change all the include file names to lower case, then things behave as expected.

[Chris Bartz , 19 October 1998]
A:     

It seems that some sort of change to recognize case insensitivity would be a useful change for NT.

It'd be best, however, to change the directory and file handling modules to generically convert all file names to lower case.

[Bob Sidebotham , 19 October 1998]

10.4. How do I build a DLL using Cons?

TO BE WRITTEN. (There is some mailing list discussion of this topic that needs editing, in case anyone cares to volunteer.)


Subject: 11. Extending Cons

11.1. How do I add support for new file types (suffixes)?

Q:     

Cons has a lot of hard-wired knowledge about building linking object files and executable files from C code. Are there suffix rules for different kinds of source files, like make does?

A:     

As long as you're willing to explicitly list all the targets to which your suffix rule will apply, you can use the following in your Construct:

                sub cons::SufRule
                {
                  my($env, $frsuf, $tosuf, @srcs) = @_;
                  my($rule) = pop @srcs;
                  foreach (@srcs) {
                    s/$frsuf$// || next;
                    Command $env "$_$tosuf", "$_$frsuf", $rule;
                  }
                }
        

Then your Conscript can do things like so:

                $CONS = new cons;

                SufRule $CONS ".pl" => ".c", @INIT, '%PERL %< > %>';
        

which allows a bunch of C files to be generated from perl scripts, or

                SufRule {$CONS->clone(CFLAGS => "-dD -E")} ".c" => ".i", @SRCS,
                        '%CCCOM';
        

which builds preprocessed versions of C files when I ask for them.

[Jeff Rosenfeld , 17 April 1998]

11.2. How do I submit a bugfix or a new feature to be added to Cons?

Short answer: Submit your patch to the mailing list (diff -c or diff -u output). After discussion, and barring objections, your patch may be incorporated into a future development release, and then into a stable release some time later.

Long answer: A number of things have to be done to make a patch ready for release. The more of these you do yourself before submitting your patch, the easier it is to incorporate, and the quicker and more likely the patch goes in. For example, if you don't document your new feature yourself, the patch will have to wait until someone else has time to document it...

An optimal patch will include:

Working code.

Cons and its regression tests work for Perl versions all the way back to 5.2. Consequently, Cons intentionally doesn't use some modern Perl idioms (like "foreach my $variable", which wasn't introduced until Perl 5.4). This means you should avoid "helpful" syntax cleanups unless you know for certain (preferably through actual testing) that your change works on all targeted Perl versions and platforms.

Updated documentation.

The POD documentation for Cons is at the end of the Cons script itself.

A new or updated regression test.

The test should demonstrate the new feature or fixed bug. That is, the current (non-patched) version of Cons should fail the test as expected, and your patched version should pass the test. (If the current version of Cons doesn't fail the test, then either the test doesn't really check for the bug you're fixing, or Cons already does what you want it to!)

Copyright assignment to the Free Software Foundation.

The Free Software Foundation requires that anyone contributing code to an FSF project (other than small two- or three-line patches) sign a document that makes the FSF the owner of any contributed work. This is to avoid any possibility of a future legal challenge to the FSF's right to distribute Cons freely.

This assignment document must be physically signed and snail-mailed to the FSF to keep the lawyers happy. The Cons maintainers (contact the FAQ author) can supply you with the appropriate document and instructions for printing it out and mailing it in.

You only have to sign this document once, and then any future patches you contribute to Cons are covered.