//--------------------------------------------------------------------------------------------------
static void BuildExecutables
(
    legato::App& app,
    const legato::BuildParams_t& buildParams
)
//--------------------------------------------------------------------------------------------------
{
    // Create an Executable Builder object.
    ExecutableBuilder_t exeBuilder(buildParams);

    // For each executable,
    auto& exeList = app.Executables();
    for (auto i = exeList.begin(); i != exeList.end(); i++)
    {
        legato::Executable& exe = i->second;

        // Put the intermediate build output files under a directory named after the executable.
        std::string objOutputDir = legato::CombinePath(buildParams.ObjOutputDir(), exe.CName());

        // Auto-generate the source code file containing main() and add it to the default component.
        exeBuilder.GenerateMain(exe, objOutputDir);

        // Build the executable.
        exeBuilder.Build(exe, objOutputDir);
    }
}
//--------------------------------------------------------------------------------------------------
static void GenerateIpcBindingConfig
(
    std::ofstream& cfgStream,
    legato::App& app,
    const legato::BuildParams_t& buildParams ///< Build parameters, such as the "is verbose" flag.
)
//--------------------------------------------------------------------------------------------------
{
    // Create nodes under "bindings", where each binding has its own node, named with the client
    // interface service name.
    cfgStream << "  \"bindings\"" << std::endl;
    cfgStream << "  {" << std::endl;

    // If cross-building for an embedded target (not "localhost"),
    if (buildParams.Target() != "localhost")
    {
        // Add a bind to the Log Client interface of the Log Control Daemon (which runs as root).
        GenerateSingleApiBindingToUser(cfgStream,
                                       "LogClient",
                                       "root",
                                       "LogClient");
    }

    // Add all the binds that were specified in the .adef file or .sdef file for this app.
    for (const auto& mapEntry : app.ExternalApiBinds())
    {
        GenerateApiBindConfig(cfgStream, app, mapEntry.second);
    }
    for (const auto& mapEntry : app.InternalApiBinds())
    {
        GenerateApiBindConfig(cfgStream, app, mapEntry.second);
    }

    cfgStream << "  }" << std::endl << std::endl;
}
示例#3
0
//--------------------------------------------------------------------------------------------------
static void Build
(
    legato::Executable& exe
)
//--------------------------------------------------------------------------------------------------
{
    // Set the target-specific environment variables (e.g., LEGATO_TARGET).
    mk::SetTargetSpecificEnvVars(BuildParams.Target());

    // Auto-generate the source code file containing main() and add it to the default component.
    ExecutableBuilder_t exeBuilder(BuildParams);
    exeBuilder.GenerateMain(exe);

    // Build all the components.
    ComponentBuilder_t componentBuilder(BuildParams);
    for (auto componentInstance : exe.ComponentInstanceList())
    {
        // Generate the IPC import/export code.
        componentBuilder.GenerateInterfaceCode(componentInstance.GetComponent());

        // Build the component.
        componentBuilder.Build(componentInstance.GetComponent());
    }

    // Do the final build step for the executable.
    // Note: All the components need to be built before this.
    exeBuilder.Build(exe);
}
示例#4
0
//--------------------------------------------------------------------------------------------------
static void BuildStandAlone
(
    void
)
//--------------------------------------------------------------------------------------------------
{
    // Create an Interface Builder object.
    InterfaceBuilder_t interfaceBuilder(BuildParams);

    // Build the IPC API libs.
    for (auto& mapEntry : Component.ProvidedApis())
    {
        auto& interface = mapEntry.second;

        // We want the generated code and other intermediate output files to go into a separate
        // interface-specific directory to avoid confusion.
        interfaceBuilder.Build(interface, legato::CombinePath(BuildParams.ObjOutputDir(),
                                                              interface.InternalName()));

        // Add the interface instance library to the list of libraries to link the component
        // library with.
        Component.AddRequiredLib(interface.Lib().ShortName());
    }

    for (auto& mapEntry : Component.RequiredApis())
    {
        auto& interface = mapEntry.second;

        if (!interface.TypesOnly()) // If only using types, don't need a library.
        {
            // We want the generated code and other intermediate output files to go into a separate
            // interface-specific directory to avoid confusion.
            interfaceBuilder.Build(interface, legato::CombinePath(BuildParams.ObjOutputDir(),
                                                                  interface.InternalName()));

            // Add the interface instance library to the list of libraries to link the component
            // library with.
            Component.AddRequiredLib(interface.Lib().ShortName());
        }
    }

    // Build the component library.
    Build();
}
示例#5
0
//--------------------------------------------------------------------------------------------------
static void Build
(
    void
)
//--------------------------------------------------------------------------------------------------
{
    // Create a Component Builder object and use it to build the component library.
    ComponentBuilder_t componentBuilder(BuildParams);
    componentBuilder.Build(Component, BuildParams.ObjOutputDir());
}
示例#6
0
//--------------------------------------------------------------------------------------------------
void MakeSystem
(
    int argc,           ///< Count of the number of command line parameters.
    const char** argv   ///< Pointer to an array of pointers to command line argument strings.
)
//--------------------------------------------------------------------------------------------------
{
    GetCommandLineArgs(argc, argv);

    // Set the target-specific environment variables (e.g., LEGATO_TARGET).
    mk::SetTargetSpecificEnvVars(BuildParams.Target());

    // Parse the .sdef file, populating the System object with the results.
    legato::parser::ParseSystem(&System, BuildParams);

    Build();
}
示例#7
0
//--------------------------------------------------------------------------------------------------
static void GenerateSystemConfig
(
    const std::string& stagingDirPath
)
//--------------------------------------------------------------------------------------------------
{
    // Open the bindings file for writing
    std::string path = stagingDirPath + "/bindings";

    if (BuildParams.IsVerbose())
    {
        std::cout << "Writing non-app bindings to file '" << path << "'."
                  << std::endl;
    }

    std::ofstream cfgStream(path, std::ofstream::trunc);

    // For each binding in the System object's list,
    for (auto bindIter : System.ApiBinds())
    {
        auto& bind = bindIter.second;

        // If the client is a non-app user,
        // Write an entry into the bindings file for this binding.
        if (! bind.IsClientAnApp())
        {
            cfgStream << '<' << bind.ClientUserName() << ">."
                      << bind.ClientInterfaceName() << " -> ";

            if (bind.IsServerAnApp())
            {
                cfgStream << bind.ServerAppName() << ".";
            }
            else
            {
                cfgStream << "<" << bind.ServerUserName() << ">.";
            }

            cfgStream << bind.ServerInterfaceName() << std::endl;
        }
    }
}
//--------------------------------------------------------------------------------------------------
static void GenerateSystemConfig
(
    const std::string& stagingDirPath,  ///< Path to the root of the app's staging directory.
    legato::App& app,                   ///< The app to generate the configuration for.
    const legato::BuildParams_t& buildParams ///< Build parameters, such as the "is verbose" flag.
)
//--------------------------------------------------------------------------------------------------
{
    // TODO: Rename this file to something that makes more sense (like "system.cfg", because it
    // gets installed in the "system" config tree).
    std::string path = stagingDirPath + "/root.cfg";

    if (buildParams.IsVerbose())
    {
        std::cout << "Generating system configuration data for app '" << app.Name() << "' in file '"
                  << path << "'." << std::endl;
    }

    std::ofstream cfgStream(path, std::ofstream::trunc);

    cfgStream << "{" << std::endl;

    GenerateAppVersionConfig(cfgStream, app);

    GenerateAppLimitsConfig(cfgStream, app);

    GenerateGroupsConfig(cfgStream, app);

    GenerateFileMappingConfig(cfgStream, app);

    GenerateProcessConfig(cfgStream, app);

    GenerateIpcBindingConfig(cfgStream, app, buildParams);

    GenerateConfigTreeAclConfig(cfgStream, app);

    cfgStream << "}" << std::endl;
}
示例#9
0
//--------------------------------------------------------------------------------------------------
void MakeComponent
(
    int argc,           ///< Count of the number of command line parameters.
    const char** argv   ///< Pointer to an array of pointers to command line argument strings.
)
//--------------------------------------------------------------------------------------------------
{
    GetCommandLineArgs(argc, argv);

    // Set the target-specific environment variables (e.g., LEGATO_TARGET).
    mk::SetTargetSpecificEnvVars(BuildParams.Target());

    ConstructObjectModel();

    if (IsStandAlone)
    {
        BuildStandAlone();
    }
    else
    {
        Build();
    }
}
示例#10
0
//--------------------------------------------------------------------------------------------------
static void GetCommandLineArgs
(
    int argc,
    const char** argv
)
//--------------------------------------------------------------------------------------------------
{
    // The target device (e.g., "ar7").
    std::string target = "localhost";

    // Non-zero = say what we are doing on stdout.
    bool isVerbose = false;

    // Full path of the library file to be generated. "" = use default file name.
    std::string buildOutputPath = "";

    // Path to the directory where generated runtime libs should be put.
    std::string libOutputDir = ".";

    // Path to the directory where intermediate build output files (such as generated
    // source code and object code files) should be put.
    std::string objOutputDir = ".";

    std::string cFlags;    // C compiler flags.
    std::string cxxFlags;  // C++ compiler flags.
    std::string ldFlags;   // Linker flags.

    // Lambda function that gets called once for each occurence of the --cflags (or -C)
    // argument on the command line.
    auto cFlagsPush = [&](const char* arg) { cFlags += " ";  cFlags += arg; };

    // Lambda function that gets called for each occurence of the --cxxflags, (or -X) argument on
    // the command line.
    auto cxxFlagsPush = [&](const char* arg) { cxxFlags += " ";  cxxFlags += arg; };

    // Lambda function that gets called once for each occurence of the --ldflags (or -L)
    // argument on the command line.
    auto ldFlagsPush = [&](const char* arg) { ldFlags += " ";  ldFlags += arg; };

    // Lambda functions for handling arguments that can appear more than once on the
    // command line.
    auto interfaceDirPush = [&](const char* path)
        {
            BuildParams.AddInterfaceDir(path);
        };
    auto sourceDirPush = [&](const char* path)
        {
            BuildParams.AddSourceDir(path);
        };

    // Lambda function that gets called once for each occurence of a component path on the
    // command line.
    auto componentPathSet = [&](const char* param)
    {
        static bool matched = false;
        if (matched)
        {
            throw legato::Exception("Only one component allowed. First is '" + Component.Path()
                                    + "'.  Second is '" + param + "'.");
        }
        matched = true;

        Component.Path(param);
    };

    // Register all our arguments with the argument parser.
    le_arg_AddOptionalString(&buildOutputPath,
                             "",
                             'o',
                             "output-path",
                             "Specify the complete path name of the component library to be built.");

    le_arg_AddOptionalString(&libOutputDir,
                             ".",
                             'l',
                             "lib-output-dir",
                             "Specify the directory into which any generated runtime libraries"
                             " should be put.  (This option ignored if -o specified.)");

    le_arg_AddOptionalString(&objOutputDir,
                             "./_build",
                             'w',
                             "object-dir",
                             "Specify the directory into which any intermediate build artifacts"
                             " (such as .o files and generated source code files) should be put.");

    le_arg_AddOptionalString(&target,
                             "localhost",
                             't',
                             "target",
                             "Specify the target device to build for (localhost | ar7).");

    le_arg_AddMultipleString('i',
                             "interface-search",
                             "Add a directory to the interface search path.",
                             interfaceDirPush);

    le_arg_AddMultipleString('c',
                             "component-search",
                             "(DEPRECATED) Add a directory to the source search path (same as -s).",
                             sourceDirPush);

    le_arg_AddMultipleString('s',
                             "source-search",
                             "Add a directory to the source search path.",
                             sourceDirPush);

    le_arg_AddOptionalFlag(&isVerbose,
                           'v',
                           "verbose",
                           "Set into verbose mode for extra diagnostic information.");

    le_arg_AddMultipleString('C',
                             "cflags",
                             "Specify extra flags to be passed to the C compiler.",
                             cFlagsPush);

    le_arg_AddMultipleString('X',
                             "cxxflags",
                             "Specify extra flags to be passed to the C++ compiler.",
                             cxxFlagsPush);

    le_arg_AddMultipleString('L',
                             "ldflags",
                             "Specify extra flags to be passed to the linker when linking "
                             "executables.",
                             ldFlagsPush);

    le_arg_AddOptionalFlag(&IsStandAlone,
                           'a',
                           "stand-alone",
                           "Create IPC interface instance libraries for APIs required by"
                           " the component and link the component library with those interface"
                           " libraries, so that the component library can be loaded and run"
                           " without the help of mkexe or mkapp.  This is useful when integrating"
                           " with third-party code that uses some other build system." );


    // Any remaining parameters on the command-line are treated as a component path.
    // Note: there should only be one.
    le_arg_SetLooseParamHandler(componentPathSet);

    // Scan the arguments now.
    le_arg_Scan(argc, argv);

    // Were we given a component?
    if (Component.Name() == "")
    {
        throw std::runtime_error("A component must be supplied on the command line.");
    }

    // Add the current working directory to the list of source search directories and the
    // list of interface search directories.
    BuildParams.AddSourceDir(".");
    BuildParams.AddInterfaceDir(".");

    // Store other build params specified on the command-line.
    if (isVerbose)
    {
        BuildParams.SetVerbose();
    }
    BuildParams.SetTarget(target);
    BuildParams.LibOutputDir(libOutputDir);
    BuildParams.ObjOutputDir(objOutputDir);
    BuildParams.CCompilerFlags(cFlags);
    BuildParams.CxxCompilerFlags(cxxFlags);
    BuildParams.LinkerFlags(ldFlags);
    if (buildOutputPath != "")
    {
        Component.Lib().BuildOutputPath(buildOutputPath);
    }
}
示例#11
0
//--------------------------------------------------------------------------------------------------
static void GetCommandLineArgs
(
    int argc,
    const char** argv
)
//--------------------------------------------------------------------------------------------------
{
    std::string target;

    bool isVerbose = false;

    // Path to the directory where intermediate build output files (such as generated
    // source code and object code files) should be put.
    std::string objectFilesDir;

    std::string cFlags;  // C compiler flags.
    std::string ldFlags; // Linker flags.

    // Lambda function that gets called once for each occurence of the --cflags (or -C)
    // argument on the command line.
    auto cFlagsPush = [&](const char* arg) { cFlags += " ";  cFlags += arg; };

    // Lambda function that gets called once for each occurence of the --ldflags (or -L)
    // argument on the command line.
    auto ldFlagsPush = [&](const char* arg) { ldFlags += " ";  ldFlags += arg; };

    // Lambda function that gets called once for each occurence of the interface search path
    // argument on the command line.
    auto ifPathPush = [&](const char* path)
        {
            BuildParams.AddInterfaceDir(legato::DoEnvVarSubstitution(path));
        };

    // Lambda function that gets called once for each occurence of the source search path
    // argument on the command line.
    auto sourcePathPush = [&](const char* path)
        {
            BuildParams.AddSourceDir(legato::DoEnvVarSubstitution(path));
        };

    // Lambda function that gets called once for each occurence of a .sdef file name on the
    // command line.
    auto sdefFileNameSet = [&](const char* param)
            {
                static bool matched = false;
                if (matched)
                {
                    throw legato::Exception("Only one system definition (.sdef) file allowed.");
                }
                matched = true;
                System.DefFilePath(legato::DoEnvVarSubstitution(param));
            };

    le_arg_AddOptionalString(&OutputDir,
                             ".",
                             'o',
                             "output-dir",
                             "Specify the directory into which the final, built system file"
                             "(ready to be installed on the target) should be put.");

    le_arg_AddOptionalString(&objectFilesDir,
                             "",
                             'w',
                             "object-dir",
                             "Specify the directory into which any intermediate build artifacts"
                             " (such as .o files and generated source code files) should be put.");

    le_arg_AddMultipleString('i',
                             "interface-search",
                             "Add a directory to the interface search path.",
                             ifPathPush);

    le_arg_AddMultipleString('s',
                             "source-search",
                             "Add a directory to the source search path.",
                             sourcePathPush);

    le_arg_AddOptionalString(&target,
                             "localhost",
                             't',
                             "target",
                             "Set the compile target (localhost|ar7).");

    le_arg_AddOptionalFlag(&isVerbose,
                           'v',
                           "verbose",
                           "Set into verbose mode for extra diagnostic information.");

    le_arg_AddMultipleString('C',
                             "cflags",
                             "Specify extra flags to be passed to the C compiler.",
                             cFlagsPush);

    le_arg_AddMultipleString('L',
                             "ldflags",
                             "Specify extra flags to be passed to the linker when linking "
                             "executables.",
                             ldFlagsPush);

    // Any remaining parameters on the command-line are treated as the .sdef file path.
    // Note: there should only be one parameter not prefixed by an argument identifier.
    le_arg_SetLooseParamHandler(sdefFileNameSet);

    le_arg_Scan(argc, argv);

    // Were we given an system definition?
    if (System.DefFilePath() == "")
    {
        throw std::runtime_error("A system definition must be supplied.");
    }

    // If we were not given an object file directory (intermediate build output directory) path,
    // use a subdirectory of the current working directory.
    if (objectFilesDir == "")
    {
        objectFilesDir = "./_build_" + System.Name() + "/" + target;
    }
    BuildParams.ObjOutputDir(objectFilesDir);

    // Add the directory containing the .sdef file to the list of source search directories
    // and the list of interface search directories.
    std::string systemDefFileDir = legato::GetContainingDir(System.DefFilePath());
    BuildParams.AddSourceDir(systemDefFileDir);
    BuildParams.AddInterfaceDir(systemDefFileDir);

    // Store other build params specified on the command-line.
    if (isVerbose)
    {
        BuildParams.SetVerbose();
    }
    BuildParams.SetTarget(target);
    BuildParams.CCompilerFlags(cFlags);
    BuildParams.LinkerFlags(ldFlags);
}
示例#12
0
//--------------------------------------------------------------------------------------------------
static void Build
(
    void
)
//--------------------------------------------------------------------------------------------------
{
    // Construct the working directory structure, which consists of an "obj" directory and
    // a "staging" directory.  Application bundles will be put inside the "staging" directory.
    // The "staging" directory will get tarred to become the actual system bundle.
    // The "obj" directory is for intermediate build output, like generated .c
    // files and .o files.  Under the "obj" directory each app has its own subdirectory to work in.
    if (BuildParams.IsVerbose())
    {
        std::cout << "Creating working directories under '" << BuildParams.ObjOutputDir() << "'."
                  << std::endl;
    }
    std::string objDirPath = BuildParams.ObjOutputDir() + "/obj";
    std::string stagingDirPath = BuildParams.ObjOutputDir() + "/staging";

    // Clean the staging area.
    legato::CleanDir(stagingDirPath);

    // Create the staging and working directories.
    legato::MakeDir(objDirPath);
    legato::MakeDir(stagingDirPath);

    // For each app in the system,
    for (auto& mapEntry : System.Apps())
    {
        auto& app = mapEntry.second;

        // Create an Application Builder object to use to build this app.
        // Give it the appropriate build parameters.
        legato::BuildParams_t appBuildParams(BuildParams);
        appBuildParams.ObjOutputDir(legato::CombinePath(objDirPath, app.Name()));
        appBuildParams.StagingDir(legato::CombinePath(appBuildParams.ObjOutputDir(), "staging"));
        ApplicationBuilder_t appBuilder(appBuildParams);

        // Build the app.  This should result in an application bundle appearing in the
        // staging directory.
        appBuilder.Build(app, stagingDirPath);
    }

    // TODO: Copy in metadata for use by Developer Studio.

    // Generate a configuration data file containing user-to-app and user-to-user bindings.
    GenerateSystemConfig(stagingDirPath);

    // Create the tarball file name.
    std::string outputPath = legato::CombinePath(OutputDir, System.Name());
    outputPath += "." + BuildParams.Target() + "_sys";  // Add the file name extension.
    if (!legato::IsAbsolutePath(outputPath))
    {
        outputPath = legato::GetWorkingDir() + "/" + outputPath;
    }

    // Create the tarball.
    std::string tarCommandLine = "tar cf \"" + outputPath + "\" -C \"" + stagingDirPath + "\" .";
    if (BuildParams.IsVerbose())
    {
        std::cout << "Packaging system into '" << outputPath << "'." << std::endl;
        std::cout << std::endl << "$ "<< tarCommandLine << std::endl << std::endl;
    }
    mk::ExecuteCommandLine(tarCommandLine);
}
示例#13
0
//--------------------------------------------------------------------------------------------------
static void GetCommandLineArgs
(
    int argc,
    const char** argv
)
//--------------------------------------------------------------------------------------------------
{
    // The target device (e.g., "ar7").
    std::string target = "localhost";

    // Non-zero = say what we are doing on stdout.
    int isVerbose = 0;

    // Path to the directory where generated runtime libs should be put.
    std::string libOutputDir = ".";

    // Path to the directory where intermediate build output files (such as generated
    // source code and object code files) should be put.
    std::string objOutputDir = ".";

    std::string cFlags;  // C compiler flags.
    std::string ldFlags; // Linker flags.

    // Lambda functions for handling arguments that can appear more than once on the
    // command line.
    auto interfaceDirPush = [&](const char* path) { BuildParams.AddInterfaceDir(path); };
    auto componentDirPush = [&](const char* path) { BuildParams.AddComponentDir(path); };
    auto contentPush = [&](const char* param) { ContentNames.push_back(param); };

    // Register all our arguments with the argument parser.
    le_arg_AddString(&ExePath,
                     'o',
                     "output",
                     "The path of the executable file to generate.");

    le_arg_AddOptionalString(&libOutputDir,
                             ".",
                             'l',
                             "lib-output-dir",
                             "Specify the directory into which any generated runtime libraries"
                             " should be put.");

    le_arg_AddOptionalString(&objOutputDir,
                             "./_build",
                             'w',
                             "object-dir",
                             "Specify the directory into which any intermediate build artifacts"
                             " (such as .o files and generated source code files) should be put.");

    le_arg_AddOptionalString(&target,
                             "localhost",
                             't',
                             "target",
                             "Specify the target device to build for (localhost | ar7).");

    le_arg_AddMultipleString('i',
                             "interface-search",
                             "Add a directory to the interface search path.",
                             interfaceDirPush);

    le_arg_AddMultipleString('c',
                             "component-search",
                             "Add a directory to the component search path.",
                             componentDirPush);

    le_arg_AddOptionalFlag(&isVerbose,
                           'v',
                           "verbose",
                           "Set into verbose mode for extra diagnostic information.");

    le_arg_AddOptionalString(&cFlags,
                             "",
                             'C',
                             "cflags",
                             "Specify extra flags to be passed to the C compiler.");

    le_arg_AddOptionalString(&ldFlags,
                             "",
                             'L',
                             "ldflags",
                             "Specify extra flags to be passed to the linker when linking "
                             "executables and libraries.");


    // Any remaining parameters on the command-line are treated as content items to be included
    // in the executable.
    le_arg_SetLooseParamHandler(contentPush);

    // Scan the arguments now.
    le_arg_Scan(argc, argv);

    // Add the current working directory to the list of component search directories and the
    // list of interface search directories.
    BuildParams.AddComponentDir(".");
    BuildParams.AddInterfaceDir(".");

    // Add the Legato framework include directory to the include path so people don't have
    // to keep doing it themselves.
    BuildParams.AddInterfaceDir("$LEGATO_ROOT/framework/c/inc");

    // Store other build params specified on the command-line.
    if (isVerbose)
    {
        BuildParams.SetVerbose();
    }
    BuildParams.SetTarget(target);
    BuildParams.LibOutputDir(libOutputDir);
    BuildParams.ObjOutputDir(objOutputDir);
    BuildParams.CCompilerFlags(cFlags);
    BuildParams.LinkerFlags(ldFlags);
}
示例#14
0
//--------------------------------------------------------------------------------------------------
static legato::Executable& ConstructObjectModel
(
    void
)
//--------------------------------------------------------------------------------------------------
{
    bool errorFound = false;

    // Create a new Executable object.
    legato::Executable& exe = App.CreateExecutable(ExePath);

    if (BuildParams.IsVerbose())
    {
        std::cout << "Making executable '" << exe.OutputPath() << "'" << std::endl
                  << "\t(using exe name '" << exe.CName() << "')" << std::endl
                  << "\tcontaining:" << std::endl;
    }

    // For each item of content, we have to figure out what type of content it is and
    // handle it accordingly.
    for (auto contentName: ContentNames)
    {
        const char* contentType;

        if (legato::IsCSource(contentName))
        {
            contentType = "C source code";

            // Add the source code file to the default component.
            exe.AddCSourceFile(legato::FindFile(contentName, BuildParams.ComponentDirs()));
        }
        else if (legato::IsLibrary(contentName))
        {
            contentType = "library";

            // Add the library file to the list of libraries to be linked with the default
            // component.
            exe.AddLibrary(contentName);
        }
        else if (legato::IsComponent(contentName, BuildParams.ComponentDirs()))
        {
            contentType = "component";

            // Find the component and add it to the executable's list of component instances.
            // NOTE: For now, we only support one instance of a component per executable, and it is
            //       identified by the file system path to that component (relative to a directory
            //       somewhere in the component search path).
            legato::Component& component = GetComponent(contentName);
            exe.AddComponentInstance(legato::ComponentInstance(component));
        }
        else
        {
            contentType = "** unknown **";

            std::cerr << "** ERROR: Couldn't identify content item '"
                      << contentName << "'." << std::endl;

            errorFound = true;
        }

        if (BuildParams.IsVerbose())
        {
            std::cout << "\t\t'" << contentName << "' (" << contentType << ")" << std::endl;
        }
    }

    if (errorFound)
    {
        throw std::runtime_error("Unable to identify requested content.");
    }

    return exe;
}