def rule_set_cpp_std(cmake):
        """ Set std=c++14 """
        if not (CMakeListsPorter.set_cpp_std_definition(cmake)
                or CMakeListsPorter.set_cpp_std_flag(cmake)):

            #Create c++ standard
            command = [
                cmkp.BlankLine(),
                cmkp.Comment("# Add support for C++14"),
                cmkp.Command("if",
                             [cmkp.Arg("NOT"),
                              cmkp.Arg("CMAKE_CXX_STANDARD")]),
                cmkp.Command("set",
                             [cmkp.Arg("CMAKE_CXX_STANDARD"),
                              cmkp.Arg("14")]),
                cmkp.Command("endif", []),
                cmkp.BlankLine()
            ]

            try:
                insert_index, _ = next(
                    commands_with(name="project", from_cmake=cmake))
            except StopIteration:
                raise ValueError(
                    "Unable to find project declaration in CMakeLists")

            for increment, item in enumerate(command):
                cmake.insert(insert_index + 1 + increment, item)
 def test_rule_set_cpp_std(self):
     before_cmake = [cmkp.Command("project", [cmkp.Arg("project_name")])]
     after_cmake = [cmkp.Command("project", [cmkp.Arg("project_name")]),
            cmkp.BlankLine(),
            cmkp.Comment("# Add support for C++14"),
            cmkp.Command("if", [cmkp.Arg("NOT"), cmkp.Arg("CMAKE_CXX_STANDARD")]),
            cmkp.Command("set", [cmkp.Arg("CMAKE_CXX_STANDARD"), cmkp.Arg("14")]),
            cmkp.Command("endif", []),
            cmkp.BlankLine()
           ]
     CMakeListsPorter.rule_set_cpp_std(before_cmake)
     self.assertEqual(before_cmake, after_cmake)
Example #3
0
 def test_rule_update_dependencies_project(self):
     before_cmake = [
         cmkp.Command("project", [cmkp.Arg("proj")]),
     ]
     after_cmake = [
         cmkp.Command("project", [cmkp.Arg("proj")]),
         cmkp.Command("find_package",
                      [cmkp.Arg("ament_cmake"),
                       cmkp.Arg("REQUIRED")]),
         cmkp.Command("set", [
             cmkp.Arg("INCLUDE_DIRS"),
             cmkp.Arg("${ament_cmake_INCLUDE_DIRS}")
         ]),
         cmkp.Command("include_directories", [cmkp.Arg("${INCLUDE_DIRS}")]),
         cmkp.Command("set", [
             cmkp.Arg("LIBRARY_DIRS"),
             cmkp.Arg("${ament_cmake_LIBRARIES}")
         ]),
         cmkp.Command(
             "set",
             [cmkp.Arg("LIBS"),
              cmkp.Arg("${ament_cmake_LIBRARIES}")]),
         cmkp.BlankLine(),
         cmkp.Command("ament_export_dependencies",
                      [cmkp.Arg("ament_cmake")])
     ]
     CMakeListsPorter.rule_update_dependencies(before_cmake)
     self.assertEqual(before_cmake, after_cmake)
    def rule_update_dependencies(cmake):
        """ Adds commands required to find, include, and link dependencies"""
        catkin_depends = CMakeListsPorter.get_catkin_dependencies(cmake)
        #catkin_depends.append("rmw_implementation")

        try:
            find_packages = next(
                commands_with(name="find_package", from_cmake=cmake))
        except StopIteration:
            find_packages = None

        #Delete old dependencies
        deleteable_commands = [
            "find_package", "include_directories", "target_link_libraries"
        ]

        CMakeListsPorter.delete_commands_with(names=deleteable_commands,
                                              from_cmake=cmake)

        #The add_executable command must come before the target_link_libraries command
        #It's okay if it comes before find_packages and include_directories
        #so all of these commands are just placed after add_executable
        add_executables = list(
            commands_with(name="add_executable", from_cmake=cmake))

        if add_executables:
            insert_index = add_executables[-1][0]
        elif find_packages:  #There were no add_executables so just insert where the old find_package was
            insert_index = find_packages[0]
        else:  #no find_packages either so use the project declaration which all valid CMakeLists should have
            insert_index, _ = next(
                commands_with(name="project", from_cmake=cmake))

        next_index = CMakeListsPorter.add_ament_dependencies(
            cmake=cmake,
            dependencies=catkin_depends,
            insert_index=insert_index + 1)

        msg_dependencies = list(
            filter(lambda pkg: pkg.endswith("_msgs") or pkg.endswith("srvs"),
                   catkin_depends))

        CMakeListsPorter.update_messages_services(
            cmake=cmake,
            msg_dependencies=msg_dependencies,
            insert_index=next_index)

        #Export dependencies
        cmake.append(cmkp.BlankLine())
        for pkg in catkin_depends:
            cmake.append(
                cmkp.Command("ament_export_dependencies", [cmkp.Arg(pkg)]))
 def test_rule_update_dependencies_execute(self):
     before_cmake = [cmkp.Command("project", [cmkp.Arg("proj")]),
                     cmkp.Command("find_package", [cmkp.Arg("roscpp")]),
                     cmkp.Command("add_executable", [cmkp.Arg("exec")])]
     after_cmake = [cmkp.Command("project", [cmkp.Arg("proj")]),
                    cmkp.Command("add_executable", [cmkp.Arg("exec")]), 
                    cmkp.Command("find_package", [cmkp.Arg("ament_cmake"), cmkp.Arg("REQUIRED")]), 
                    cmkp.Command("find_package", [cmkp.Arg("rclcpp"), cmkp.Arg("REQUIRED")]), 
                    cmkp.Command("set", [cmkp.Arg("INCLUDE_DIRS"), cmkp.Arg("${ament_cmake_INCLUDE_DIRS}"), cmkp.Arg("${rclcpp_INCLUDE_DIRS}")]), 
                    cmkp.Command("include_directories", [cmkp.Arg("${INCLUDE_DIRS}")]), 
                    cmkp.Command("set", [cmkp.Arg("LIBRARY_DIRS"), cmkp.Arg("${ament_cmake_LIBRARIES}"), cmkp.Arg("${rclcpp_LIBRARIES}")]),
                    cmkp.Command("target_link_libraries", [cmkp.Arg("exec"), cmkp.Arg("${LIBRARY_DIRS}")]), 
                    cmkp.Command("set", [cmkp.Arg("LIBS"), cmkp.Arg("${ament_cmake_LIBRARIES}"), cmkp.Arg("${rclcpp_LIBRARIES}")]), 
                    cmkp.BlankLine(), 
                    cmkp.Command("ament_export_dependencies", [cmkp.Arg("ament_cmake")]), 
                    cmkp.Command("ament_export_dependencies", [cmkp.Arg("rclcpp")])]
     CMakeListsPorter.rule_update_dependencies(before_cmake)
     self.assertEqual(before_cmake, after_cmake)
    def port(content, extra_rules=[], extra_warnings=[]):
        """
        Arguments:
            content - A string with contents of CMakeLists.txt
            extra_rules - a list of functions (rules) to apply to the content
            extra_warnings - a list of functions to check for warnings in the content

        Returns:
            A new string with the updated CMakeLists
        """

        cmake = cmkp.parse(content)

        #Pulls out all methods in this class with name starting with "rule"
        rules = get_functions_with(
            criteria=lambda name: name.startswith("rule"),
            from_class=CMakeListsPorter)

        for rule in rules + extra_rules:
            rule(cmake)

        #Pulls out all methods in this class with name starting with "warn"
        warnings = get_functions_with(
            criteria=lambda name: name.startswith("warn"),
            from_class=CMakeListsPorter)

        for warning in warnings + extra_warnings:
            warning(cmake)

        # This command must be at the bottom of the package
        cmake.append(cmkp.BlankLine())
        ament_package_cmd = cmkp.Command("ament_package", [])
        cmake.append(ament_package_cmd)

        cmake_contents = '\n'.join(
            cmkp.compose_lines(cmake, cmkp.FormattingOptions())) + '\n'
        #Post-process text
        #replace variable names:
        for var, replacement in CatkinToAmentMigration.CMAKE_LISTS_RENAMED_VARIABLES.items(
        ):
            cmake_contents = cmake_contents.replace(var, replacement)

        return cmake_contents
Example #7
0
 def __addBlankLine(cls, cmake, index):
     index += 1
     cmake.insert(index, cmkp.BlankLine())
     return index
Example #8
0
    def port(cls, dryrun=False):
        # Make sure the file exists (even though this is already checked)
        if not exists(CMAKELISTS):
            print("ERROR: Unable to locate CMakeLists.txt")
            return False

        # Read the file content
        fid = open(CMAKELISTS, "r")
        content = fid.read()
        fid.close()

        # Parse the cmake content
        cmake = cmkp.parse(content)

        # Item indices to remove from the file
        removeIndices = []

        # List of catkin packages this package depends on
        catkinDepends = []

        # Libraries created by this package
        packageLibs = []

        # Message and service files to generate
        msgsAndSrvs = []

        projectDeclIndex = -1
        hasCpp11 = False
        for index, item in enumerate(cmake):
            # Skip non commmand items
            if type(item) != type(cmkp.Command("a", "b")):
                continue

            # Grab names of all arguments to this command
            args = [b.contents for b in item.body]

            if item.name == "project":
                projectDeclIndex = index
            elif item.name == "add_definitions":
                # Handle C++11 support added through definitions
                if "-std=c++11" in args:
                    hasCpp11 = True
            elif item.name == "set":
                # Handle C++11 support specifically set to CXX flags
                if "CMAKE_CXX_FLAGS" in args:
                    for arg in args:
                        if "-std=c++11" in arg:
                            hasCpp11 = True
                            break
            elif item.name == "find_package":
                if len(args) > 0 and "catkin" == args[0]:
                    removeIndices.append(index)

                    # An example of command is:
                    #     find_package(catkin REQUIRED COMPONENTS pkg1 pkg2)
                    if "COMPONENTS" in args:
                        componentsStart = args.index("COMPONENTS")
                        catkinDepends = args[componentsStart + 1:]

                        generatesMessages = ("message_generation" in args)

                        # Remove packages that no longer exist in ROS 2
                        catkinDepends = list(
                            filter(lambda p: p not in UNKNOWN_ROS_PACKAGES,
                                   catkinDepends))

                        # Handle packages that have been renamed in ROS 2
                        catkinDepends = list(
                            map(lambda p: RENAMED_ROS_PACKAGES.get(p, p),
                                catkinDepends))

                        # Add additional packages needed for message generation
                        if generatesMessages:
                            catkinDepends.extend([
                                "rosidl_default_generators",
                                "rosidl_default_runtime",
                            ])
            elif item.name == "catkin_package":
                # Remove the catkin_packge element
                removeIndices.append(index)
            elif item.name == "include_directories":
                # Remove the include directories, which will be added later
                removeIndices.append(index)
            elif item.name == "link_directories":
                # Remove the link directories, which will be added later
                removeIndices.append(index)
            elif item.name == "catkin_destinations":
                # Remove this command as it no longer exists
                removeIndices.append(index)
            elif item.name == "catkin_metapackage":
                # Remove this command as it no longer exists
                removeIndices.append(index)
            elif item.name == "catkin_python_setup":
                # Remove this command as it no longer exists
                removeIndices.append(index)
            elif item.name == "add_dependencies":
                # The catkin exported targets variable no longer exists,
                # so remove this
                if "${catkin_EXPORTED_TARGETS}" in args:
                    removeIndices.append(index)
            elif item.name == "target_link_libraries":
                # Replace the reference to catkin libraries (which no longer
                # exists) with a reference to libraries variable
                if "${catkin_LIBRARIES}" in args:
                    catkinIndex = args.index("${catkin_LIBRARIES}")
                    item.body[catkinIndex] = cmkp.Arg("${LIBS}")
            elif item.name == "add_library":
                # Keep track of the names of all libraries created
                # by this package
                if len(args) > 0:
                    packageLibs.append(args[0])
            elif item.name == "add_message_files":
                if len(args) > 1:
                    msgFiles = list(map(lambda s: "msg/%s" % s, args[1:]))
                    msgsAndSrvs.extend(msgFiles)

                # Remove this command as it has been replaced
                removeIndices.append(index)
            elif item.name == "add_service_files":
                if len(args) > 1:
                    serviceFiles = list(map(lambda s: "srv/%s" % s, args[1:]))
                    msgsAndSrvs.extend(serviceFiles)

                # Remove this command as it has been replaced
                removeIndices.append(index)
            elif item.name == "generate_messages":
                # Remove this command as it has been replaced
                removeIndices.append(index)

        # Should never happen, but just in case...
        if projectDeclIndex == -1:
            print("ERROR: Failed to locate project declaration!")
            return False

        # Remove all indices in reverse sorted order to prevent the
        # indices from changing values
        for index in sorted(removeIndices, reverse=True):
            del cmake[index]

        # Make sure C++11 support is added
        if not hasCpp11:
            comment = cmkp.Comment("# Add support for C++11")

            openIf = cmkp.Command("if", [cmkp.Arg("NOT"), cmkp.Arg("WIN32")])

            addDef = cmkp.Command("add_definitions",
                                  [cmkp.Arg(contents="-std=c++11")])

            closeIf = cmkp.Command("endif", [])

            # Add all the items
            items = [
                cmkp.BlankLine(),
                comment,
                openIf,
                addDef,
                closeIf,
                cmkp.BlankLine(),
            ]
            for item in items:
                projectDeclIndex += 1
                cmake.insert(projectDeclIndex, item)

        ## Add all find_package calls

        # Must find the ament_cmake package
        catkinDepends.insert(0, "ament_cmake")

        # Add calls to find all other dependency packages
        for pkg in catkinDepends:
            findPkg = cls.__findPackage(pkg)
            projectDeclIndex += 1
            cmake.insert(projectDeclIndex, findPkg)

        # Add a blank line
        projectDeclIndex = cls.__addBlankLine(cmake, projectDeclIndex)

        ## Handle message generation
        if len(msgsAndSrvs) > 0:
            # rosidl_generate_interfaces(urg_node_msgs
            #    "msg/Status.msg"
            #    DEPENDENCIES builtin_interfaces std_msgs
            # )
            cmdArgs = [cmkp.Arg("${PROJECT_NAME}")]
            for filename in msgsAndSrvs:
                cmdArgs.append(cmkp.Arg('"%s"' % filename))

            # Add dependencies on message packages
            cmdArgs.extend([
                cmkp.Arg("DEPENDENCIES"),
                cmkp.Arg("builtin_interfaces"),
            ])
            for pkg in catkinDepends:
                if pkg.endswith("_msgs") or pkg.endswith("srvs"):
                    cmdArgs.append(cmkp.Arg(pkg))

            genIfaceCmd = cmkp.Command("rosidl_generate_interfaces", cmdArgs)
            projectDeclIndex += 1
            cmake.insert(projectDeclIndex, genIfaceCmd)

            # Add a blank line
            projectDeclIndex = cls.__addBlankLine(cmake, projectDeclIndex)

        ## Define variables for all include dirs and libraries
        includeArgs = [cmkp.Arg("INCLUDE_DIRS")]
        libArgs = [cmkp.Arg("LIBS")]
        libDirArgs = [cmkp.Arg("LIBRARY_DIRS")]
        for pkg in catkinDepends:
            includeArgs.append(cmkp.Arg("${%s_INCLUDE_DIRS}" % pkg))
            libArgs.append(cmkp.Arg("${%s_LIBRARIES}" % pkg))
            libDirArgs.append(cmkp.Arg("${%s_LIBRARY_DIRS}" % pkg))

        # If an include directory exists for this package, add it to
        # the include dirs
        if exists("include"):
            includeArgs.insert(1, cmkp.Arg("include"))

        # Add command to set include dirs
        setIncludeDirs = cmkp.Command("set", includeArgs)
        projectDeclIndex += 1
        cmake.insert(projectDeclIndex, setIncludeDirs)

        ## Add the include_directories command
        includeDirs = cmkp.Command("include_directories",
                                   [cmkp.Arg("${INCLUDE_DIRS}")])
        projectDeclIndex += 1
        cmake.insert(projectDeclIndex, includeDirs)
        projectDeclIndex = cls.__addBlankLine(cmake, projectDeclIndex)

        # Add command to set lib dirs
        setLibDirs = cmkp.Command("set", libDirArgs)
        projectDeclIndex += 1
        cmake.insert(projectDeclIndex, setLibDirs)
        projectDeclIndex = cls.__addBlankLine(cmake, projectDeclIndex)

        ## Add the link_directories command
        linkDirs = cmkp.Command("link_directories",
                                [cmkp.Arg("${LIBRARY_DIRS}")])
        projectDeclIndex += 1
        cmake.insert(projectDeclIndex, linkDirs)
        projectDeclIndex = cls.__addBlankLine(cmake, projectDeclIndex)

        # Add command to set libs
        setLibs = cmkp.Command("set", libArgs)
        projectDeclIndex += 1
        cmake.insert(projectDeclIndex, setLibs)
        projectDeclIndex = cls.__addBlankLine(cmake, projectDeclIndex)

        ## Export all ament dependencies at the bottom of the file
        cmake.append(cmkp.BlankLine())
        for pkg in catkinDepends:
            export = cmkp.Command("ament_export_dependencies", [cmkp.Arg(pkg)])
            cmake.append(export)

        ## Export include directories
        exportIncludes = cmkp.Command("ament_export_include_directories",
                                      [cmkp.Arg("${INCLUDE_DIRS}")])
        cmake.append(exportIncludes)

        ## Export all known libraries
        if len(packageLibs) > 0:
            exportLibs = cmkp.Command("ament_export_libraries",
                                      [cmkp.Arg(lib) for lib in packageLibs])
            cmake.append(exportLibs)

        # Add the final call to initialize the ament package
        # (this must be at the bottom of the file!)
        projectDeclIndex = cmake.append(cmkp.BlankLine())
        amentPackageCmd = cmkp.Command("ament_package", [])
        cmake.append(amentPackageCmd)

        # Remove any double blank lines
        index = 0
        while index < (len(cmake) - 1):
            isBlank = lambda i: (type(i) == type(cmkp.BlankLine()))
            item = cmake[index]
            nextItem = cmake[index + 1]

            if isBlank(item) and isBlank(nextItem):
                del cmake[index]
                continue  # Repeat this index

            index += 1

        # Convert the CMakeLists content into a nicely formatted string
        cmakeData = '\n'.join(
            cmkp.compose_lines(cmake, cmkp.FormattingOptions())) + '\n'

        # Replace all instances of variables that no longer exist
        renamedVariables = {
            "${CATKIN_DEVEL_PREFIX}/": "",
            "${CATKIN_GLOBAL_BIN_DESTINATION}": "bin",
            "${CATKIN_GLOBAL_INCLUDE_DESTINATION}": "include",
            "${CATKIN_GLOBAL_LIB_DESTINATION}": "lib",
            "${CATKIN_GLOBAL_LIBEXEC_DESTINATION}": "lib",
            "${CATKIN_GLOBAL_SHARE_DESTINATION}": "share",
            "${CATKIN_PACKAGE_BIN_DESTINATION}": "lib/${PROJECT_NAME}",
            "${CATKIN_PACKAGE_INCLUDE_DESTINATION}": "include/${PROJECT_NAME}",
            "${CATKIN_PACKAGE_LIB_DESTINATION}": "lib",
            "${CATKIN_PACKAGE_SHARE_DESTINATION}": "share/${PROJECT_NAME}",
        }
        for var, replacement in renamedVariables.items():
            cmakeData = cmakeData.replace(var, replacement)

        if not dryrun:
            fid = open(CMAKELISTS, "w")
            fid.write("%s\n" % cmakeData.strip())
            fid.close()
        else:
            print(cmakeData)

        return True  # Success