Compiling Sources

MakeMe compiles source files by building library or executable targets that specify the sources to be compiled.

For example, this target will compile the sources in the current directory and create a shared library containing the compiled objects.

targets {
    libname: {
        type: 'lib',
        sources: '*.c',
    },
}

MakeMe expands the sources property and creates objects targets at run-time for each of the corresponding sources.

MakeMe also creates targets for any include files referenced by the sources. If the source file is updated, it will be recompiled and library rebuilt when MakeMe is next run. If any of the included headers are updated, the source file and library will similarly be rebuilt.

Source targets are related to their library target via a depends property. The target that specified the sources (in this example, the library) will depend on the sources. The various source targets will depend on their include header targets. When building, MakeMe will recursively consider the dependant targets and rebuild any that are out-of-date.

Any target can specify a list of sources. Typically, this will be libraries and executables, but any target can specify sources. For completeness, here is an executable target.

myProgram: {
    type: 'exe',
    sources: '*.c',
},

Selecting Source Files

The sources property may be set to a regular expression, a string or an array of either. For example:

sources: /view-.*\.c|image.*\.c/,

This will select any C source files starting with 'view-' or 'image' in the current directory.

sources: ['*.c', '*.cpp'],

This will select C and C++ source files in the current directory.

sources: '**.c',

This will select C source files in the current directory or in any sub-directory below.

Multiple sources properties can be specified by using an array of strings.

source: ['*.c', '*.cpp' ],

This is useful when the sources to build may be specified in more than one MakeMe file.

Excluding Items

You can exclude specific items from the sources list. Similar to the sources property, the exclude property can be set to a regular expression, a string or an array of either. Once the list of sources is built from the sources property, each qualifying source file is checked to see if it should be excluded. The exclusion match is performed on the entire source file name.

sources: '**.c',
exclude: /\/test$|\/demo$',

This will exclude C files in the test and demo directories. If using regular expressions, make sure you include a leading '\/' and trailing '$' if you want to match a directory basename.

Headers

MakeMe, by convention, copies declared headers to the platform OS-ARCH-PROFILE/inc directory. This is so that headers can be easily resolved when compiling by a single -I compiler flag. This is not required, but certainly recommended. You can specify headers to export to the platform directory via the headers property. For example:

libname: {
    type: 'lib',
    sources: '*.c',
    headers: [ '*.h' ],
}

This will create targets at run-time for all headers in the current directory. These targets will copy the headers (if modified) to the inc directory for the platform. The standard MakeMe compile rules will then resolve the headers from the inc directory.

Resources

MakeMe can compile Windows resources by specifying them in a resources property list. For example:

appwebMonitor: {
    enable: "me.platform.like == 'windows'",
    type: 'exe',
    rule: 'gui',
    depends: [ 'libappweb' ],
    sources: [ 'windows/*.c' ],
    headers: [ 'windows/*.h' ],                                                                    
    libraries: [ 'shell32.lib' ],
    resources: [ 'windows/appwebMonitor.rc' ],
},

This will use the Windows resource compiler rc to compile the resource and then link into the executable.

Compiler Options

MakeMe has builtin defaults for the various compiler and linker command line formats. The include properties for include directories, compiler options, compiler pre-processor definitions, link libraries, paths and options. These properties are inherited by all targets who can use, augment or modify these defaults if required. Typically, the defaults are sufficient, but they can be modified, augmented or delete on a per-target basis.

For example, to add additional switches, libraries and other compilation options:

mylib: {
    '+includes': [
        "macosx-x64-debug/inc",
    ],
    '+compiler': [
        "-fomit-frame-pointer",
    ],
    '+defines': [
        "ENABLE_TURBO_MODE",
    ],
    '+libraries': [
        "edit",
    ],
    '+libpaths': [
        "headers",
    ],
    '+linker': [
        "-Wl,-rpath,/opt/lib",
    ],
},

Here are the supported properties

PropertyPurpose
includesDirectories to search for headers
compilerCompiler options
definesCompiler pre-processor defines
librariesLibraries to link with when linking
libpathsDirectories to search for libraries
linkerLinker flags

Use a "+"" prefix when defining array values. This adds the values to the existing property set. Use - to remove a value from the existing definition. Use a an "=" prefix to replace existing values.

Note that the default link phase rules use the compiler and not the linker to create executables. As such, linker flags must be prefixed to pass through to the linker. Also note that many compiler and linker flags are platform specific and thus not-portable. Fortunately, MakeMe portably defines most common compiler and linker flags for you. If you need to portably modify these flags, you will need to use some scripting.

Target Scripting

Targets can run scripts at various stages in the build life cycle. These scripts run in response to build events. Defining a script to run in response to the precompile event will enable us to modify the compiler properties before the target is build. For example:

scripts: {
    precompile: "
        if (me.platform.os == 'windows') {
            me.target.compiler.push('-GF -Wp64')
        }
    ",
},

For detailed information on scripting, please see MakeMe Events and MakeMe Scripting.

Inheriting Defaults

Every target inherits default properties from the defaults and internal property collections. The default collection is global across the project. The internal collection is local to the current MakeMe file.

MakeMe defines the initial compiler properties as required for the current platform operating system and CPU architecture. However, you can override the default properties so that all targets inherit your customizations. For example:

defaults: {
    '+defines': [ 'ENABLE_DIRECT_X' ],
},

Private Inheritance

Similar to the defaults collection, you can specify properties that are inherited only by targets in the same MakeMe file. To do this, create an internal collection.

internal: {
    libraries: [ 'pam', 'z' ],
},

Explicit Inheritance

Sometimes you have a some settings that must be inherited by a subset of targets that are not in the same MakeMe file. MakeMe allows a target to specify a collection of settings from which to inherit via the inherit property. For example:

'shared-settings': {
    '+defines': [ 'NUMBER=42' ],
},

These shared settings can be in any MakeMe file.

rocket {
    inherit: ['shared-settings'],
    type: 'exe'
    sources: '*.c',
},

In this manner, specific targets can nominate the group of settings form which they wish to inherit.

Compiler Rules

MakeMe defines compiler command templates to use for compiling, linking and other tasks. These templates are defined in the the rules property collection. These templates are unique for each supported platform as they depend on the local compiler.

You should rarely need to modify these templates, but like the rest of MakeMe, you can modify them if you need to. Rules use as their property name, the desired transition. This is of the form: 'FROM->TO' For example, to compile a .c file into a .o, the rule name would be '.c->.o' For example:

rules: {
    'c->o': '${targets.compiler.path} -c -o ${OUT} ${CFLAGS} ${DEFINES} ${INCLUDES} ${IN}',
},

The various ${token} values are expanded by MakeMe using the compiler properties. The ${OUT} token is set to the target destination path. The ${IN} token is set to the name of the file to compile.

Examples

Here is an actual example from the Ejscript project that builds the ejs library:

libejs: {
    type: 'lib',
    headers: [ 'slots/*.h', '*.h' ],
    sources: [
        'compiler/*.c',
        'core/src/*.c',
        'vm/*.c'
    ],
    depends: [ 'libhttp' ],
},

To learn more, read about Creating Libraries.

© Embedthis Software. All rights reserved.