//--------------------------------------------------------------------------------------------------
static void BuildAndBundleComponent
(
    legato::Component& component,
    ComponentBuilder_t componentBuilder,
    const std::string& appWorkingDir
)
//--------------------------------------------------------------------------------------------------
{
    if (component.IsBuilt() == false)
    {
        // Do sub-components first.
        for (auto& mapEntry : component.SubComponents())
        {
            auto subComponentPtr = mapEntry.second;

            BuildAndBundleComponent(*subComponentPtr, componentBuilder, appWorkingDir);
        }

        // Each component gets its own object file dir.
        std::string objOutputDir = legato::CombinePath(appWorkingDir,
                                                       "component/" + component.Name());

        // Build the component.
        // NOTE: This will detect if the component doesn't actually need to be built, either because
        //       it doesn't have any source files that need to be compiled, or because they have
        //       already been compiled.
        componentBuilder.Build(component, objOutputDir);

        // Copy all the bundled files and directories from the component into the staging area.
        componentBuilder.Bundle(component);
    }
}
Beispiel #2
0
//--------------------------------------------------------------------------------------------------
void GetComponentLibLinkDirectives
(
    std::ostream& outputStream,
    const legato::Component& component
)
//--------------------------------------------------------------------------------------------------
{
    // Link with required libraries.
    for (auto lib : component.RequiredLibs())
    {
        outputStream << " -l" << lib;
    }

    // Link with bundled libraries.
    for (auto lib : component.BundledLibs())
    {
        mk::GetLinkDirectiveForLibrary(outputStream, lib);
    }

    // For each sub-component,
    for (const auto& mapEntry : component.SubComponents())
    {
        auto componentPtr = mapEntry.second;

        // If the component has itself been built into a library, link with that.
        if (componentPtr->Lib().Exists())
        {
            outputStream << " -l" << componentPtr->Lib().ShortName();
        }

        // Link with whatever this component depends on, bundles, or requires.
        GetComponentLibLinkDirectives(outputStream, *componentPtr);
    }
}
Beispiel #3
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();
}
Beispiel #4
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);
    }
}
//--------------------------------------------------------------------------------------------------
void ComponentBuilder_t::GenerateInterfaceCode
(
    legato::Component& component
)
//--------------------------------------------------------------------------------------------------
{
    // Don't do anything if the component doesn't import or export any interfaces.
    if (component.ImportedInterfaces().empty() && component.ExportedInterfaces().empty())
    {
        return;
    }

    // Generate the path to the directory in which the generated files will go.
    std::string dirPath = m_Params.ObjOutputDir()
                        + "/components/"
                        + component.Name();

    if (m_Params.IsVerbose())
    {
        std::cout << "Generating interface code for '" << component.Name()
                  << "' in directory '" << dirPath << "'." << std::endl;
    }

    // Make sure the directory exists.
    legato::MakeDir(dirPath);

    // Open the interface.h file for writing.
    std::string interfacesHeaderFilePath = legato::CombinePath(dirPath, "interfaces.h");
    std::ofstream interfaceHeaderFile(interfacesHeaderFilePath, std::ofstream::trunc);
    if (!interfaceHeaderFile.is_open())
    {
        throw legato::Exception("Failed to open file '" + interfacesHeaderFilePath + "'.");
    }

    // For each interface imported by the component, generate the client-side IPC code.
    const auto& importMap = component.ImportedInterfaces();
    for (auto i = importMap.cbegin(); i != importMap.cend(); i++)
    {
        const legato::ImportedInterface& interface = std::get<1>(*i);

        if (m_Params.IsVerbose())
        {
            std::cout << "  Generating code for imported interface '" << interface.InstanceName()
                      << "'." << std::endl;
        }

        mk::GenerateApiClientCode(interface.InstanceName(),
                                  interface.ApiFilePath(),
                                  dirPath,
                                  m_Params.IsVerbose());

        // Add the generated code to the component.
        std::string cFilePath(legato::AbsolutePath(legato::CombinePath(dirPath,
                                                                       interface.InstanceName()
                                                                         + "_client.c")));
        component.AddSourceFile(cFilePath);

        // Add the interface code's header to the component's interface.h file.
        interfaceHeaderFile << "#include \"" << interface.InstanceName() << "_interface.h" << "\""
                            << std::endl;
    }

    // For each interface exported by the component, generate the server-side IPC code.
    const auto& exportMap = component.ExportedInterfaces();
    for (auto i = exportMap.cbegin(); i != exportMap.cend(); i++)
    {
        const legato::Interface& interface = std::get<1>(*i);

        if (m_Params.IsVerbose())
        {
            std::cout << "  Generating code for exported interface '" << interface.InstanceName()
                      << "'." << std::endl;
        }

        mk::GenerateApiServerCode(interface.InstanceName(),
                                  interface.ApiFilePath(),
                                  dirPath,
                                  m_Params.IsVerbose());

        // Add the generated code to the component.
        std::string cFilePath(legato::AbsolutePath(legato::CombinePath(dirPath,
                                                                       interface.InstanceName()
                                                                         + "_server.c")));
        component.AddSourceFile(cFilePath);

        // Add the interface code's header to the component's interface.h file.
        interfaceHeaderFile << "#include \"" << interface.InstanceName() << "_server.h" << "\""
                            << std::endl;
    }

    // Add the directory to the include search path so the compiler can find the "interface.h" file.
    component.AddIncludeDir(dirPath);
}
//--------------------------------------------------------------------------------------------------
void ComponentBuilder_t::Build
(
    const legato::Component& component  ///< The component whose library is to be built.
)
//--------------------------------------------------------------------------------------------------
{
    // Essentially, when we build a component, we use gcc to build a library (.so) from a bunch
    // of C source code files.  The library goes into the component's library output directory.

    // TODO: Check if files need recompiling.  Maybe change to use intermediate .o files
    //       to break up compiling so it runs faster for large components that have had
    //       only small changes to them.

    std::stringstream commandLine;
    commandLine << mk::GetCompilerPath(m_Params.Target());

    // Specify the output file path.
    // TODO: Add a version number to the library.
    std::string libPath = m_Params.LibOutputDir() + "/lib" + component.Name() + ".so";
    if (m_Params.IsVerbose())
    {
        std::cout << "Building component library '" << libPath << "'." << std::endl;
    }
    commandLine << " -o " << libPath
                << " -shared"
                << " -fPIC"
                << " -Wall"
                << " -Werror";

    // Add the include paths specified on the command-line.
    for (auto i : m_Params.InterfaceDirs())
    {
        commandLine << " -I" << i;
    }

    // Add the include paths specific to the component.
    for (auto i : component.IncludePath())
    {
        commandLine << " -I" << i;
    }

    // Define the component name, log session variable, and log filter variable.
    commandLine << " -DLEGATO_COMPONENT=" << component.CName();
    commandLine << " -DLE_LOG_SESSION=" << component.Name() << "_LogSession ";
    commandLine << " -DLE_LOG_LEVEL_FILTER_PTR=" << component.Name() << "_LogLevelFilterPtr ";

    // Define the COMPONENT_INIT.
    commandLine << " \"-DCOMPONENT_INIT=void " << mk::GetComponentInitName(component) << "()\"";

    // Add the CFLAGS to the command-line.
    commandLine << " " << m_Params.CCompilerFlags();

    // Add the list of C source code files.
    if (component.CSourcesList().empty())
    {
        throw legato::Exception("Component '" + component.Name() + "' has no source files.");
    }
    for (const auto& sourceFile : component.CSourcesList())
    {
        commandLine << " \"" ;

        if ((component.Path() != "") && (!legato::IsAbsolutePath(sourceFile)))
        {
            commandLine << component.Path() << "/" << sourceFile;
        }
        else
        {
            commandLine << sourceFile;
        }

        commandLine << "\"" ;
    }

    // Run the command.
    if (m_Params.IsVerbose())
    {
        std::cout << commandLine.str() << std::endl;
    }
    mk::ExecuteCommandLine(commandLine);
}