//-------------------------------------------------------------------------------------------------- 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; }
//-------------------------------------------------------------------------------------------------- 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); }
//-------------------------------------------------------------------------------------------------- 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 Build ( void ) //-------------------------------------------------------------------------------------------------- { // Create a Component Builder object and use it to build the component library. ComponentBuilder_t componentBuilder(BuildParams); componentBuilder.Build(Component, BuildParams.ObjOutputDir()); }
//-------------------------------------------------------------------------------------------------- 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(); }
//-------------------------------------------------------------------------------------------------- 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; }
//-------------------------------------------------------------------------------------------------- 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(); } }
//-------------------------------------------------------------------------------------------------- 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); } }
//-------------------------------------------------------------------------------------------------- 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); }
//-------------------------------------------------------------------------------------------------- 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); }
//-------------------------------------------------------------------------------------------------- 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); }
//-------------------------------------------------------------------------------------------------- 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; }