//-------------------------------------------------------------------------------------------------- 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); } }
//-------------------------------------------------------------------------------------------------- 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); } }
//-------------------------------------------------------------------------------------------------- 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(); }
//-------------------------------------------------------------------------------------------------- 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); }