diff --git a/multibody/parsing/BUILD.bazel b/multibody/parsing/BUILD.bazel index 35d046035773..b497bfe82ddb 100644 --- a/multibody/parsing/BUILD.bazel +++ b/multibody/parsing/BUILD.bazel @@ -51,7 +51,6 @@ drake_cc_library( ], deps = [ "//common", - "@spruce", "@tinydir", "@tinyxml2", ], @@ -80,7 +79,6 @@ drake_cc_library( "//math:geometric_transform", "//multibody/plant:coulomb_friction", "@sdformat", - "@spruce", "@tinyxml2", ], ) @@ -165,6 +163,7 @@ drake_cc_library( deps = [ ":detail_sdf_parser", ":detail_urdf_parser", + "//common:filesystem", ], ) @@ -220,6 +219,7 @@ drake_cc_googletest( ], deps = [ ":parser", + "//common:filesystem", "//common:find_resource", "//common/test_utilities", ], @@ -234,9 +234,9 @@ drake_cc_googletest( ], deps = [ ":detail_sdf_parser", + "//common:filesystem", "//common:find_resource", "//common/test_utilities", - "@spruce", ], ) @@ -250,11 +250,11 @@ drake_cc_googletest( ], deps = [ ":detail_urdf_parser", + "//common:filesystem", "//common:find_resource", "//common/test_utilities", "//multibody/benchmarks/acrobot", "//multibody/benchmarks/acrobot:make_acrobot_plant", - "@spruce", ], ) @@ -287,6 +287,7 @@ drake_cc_googletest( ], deps = [ ":package_map", + "//common:filesystem", "//common:find_resource", ], ) @@ -298,6 +299,7 @@ drake_cc_googletest( ], deps = [ ":detail_misc", + "//common:filesystem", "//common/test_utilities", ], ) diff --git a/multibody/parsing/detail_path_utils.cc b/multibody/parsing/detail_path_utils.cc index 8084cd2f33e9..e92952d41376 100644 --- a/multibody/parsing/detail_path_utils.cc +++ b/multibody/parsing/detail_path_utils.cc @@ -4,10 +4,9 @@ #include #include -#include - #include "drake/common/drake_assert.h" #include "drake/common/drake_optional.h" +#include "drake/common/filesystem.h" #include "drake/common/never_destroyed.h" #include "drake/common/text_logging.h" @@ -34,19 +33,16 @@ string GetFullPath(const string& file_name) { if (IsAbsolutePath(result)) { // The specified file is already an absolute path. The following code // verifies that the file exists. - spruce::path path(file_name); - if (!path.isFile()) { + if (!filesystem::is_regular_file({file_name})) { throw std::runtime_error("drake::parsers::GetFullPath: ERROR: " "file_name \"" + file_name + "\" is not a file."); } } else { // The specified file is a relative path. The following code obtains the // full path and verifies that the file exists. - spruce::path path = spruce::dir::getcwd(); - path.append(file_name); - if (path.isFile()) { - result = path.getStr(); - } else { + result = (filesystem::current_path() / + filesystem::path(file_name)).lexically_normal().string(); + if (!filesystem::is_regular_file({result})) { throw std::runtime_error("drake::parsers::GetFullPath: ERROR: " "file_name \"" + file_name + "\" is not a file or does not exist."); } @@ -71,11 +67,11 @@ optional GetPackagePath( string ResolveUri(const string& uri, const PackageMap& package_map, const string& root_dir) { - spruce::path result; + filesystem::path result; // Parse the given URI into pieces. static const never_destroyed uri_matcher{ - "^([a-z0-9+.-]+)://([^/]*)(/+.*)" + "^([a-z0-9+.-]+)://([^/]*)/+(.*)" }; std::smatch match; if (std::regex_match(uri, match, uri_matcher.access())) { @@ -83,15 +79,14 @@ string ResolveUri(const string& uri, const PackageMap& package_map, DRAKE_DEMAND(match.size() == 4); const auto& uri_scheme = match[1]; const auto& uri_package = match[2]; - const auto& uri_path = match[3]; // This includes the leading '/' (if any). + const auto& uri_path = match[3]; if (uri_scheme == "file") { - result = uri_path.str(); + result = "/" + uri_path.str(); } else if ((uri_scheme == "model") || (uri_scheme == "package")) { optional package_path = GetPackagePath(uri_package, package_map); if (!package_path) { return {}; } - result = spruce::path(*package_path); - result.append(uri_path.str()); + result = filesystem::path(*package_path) / std::string(uri_path); } else { drake::log()->warn( "URI '{}' specifies an unsupported scheme; supported schemes are " @@ -108,18 +103,16 @@ string ResolveUri(const string& uri, const PackageMap& package_map, result = root_dir; result.append(filename); } else { - result = spruce::dir::getcwd(); - result.append(root_dir); - result.append(filename); + result = filesystem::current_path() / root_dir / filename; } } - if (!result.exists()) { + if (!filesystem::exists(result)) { drake::log()->warn("URI '{}' resolved to '{}' which could not be found.", - uri, result.getStr()); + uri, result.string()); return {}; } - return result.getStr(); + return result.string(); } } // namespace internal diff --git a/multibody/parsing/package_map.cc b/multibody/parsing/package_map.cc index 5938c5dee564..0134f4d4b5ad 100644 --- a/multibody/parsing/package_map.cc +++ b/multibody/parsing/package_map.cc @@ -2,17 +2,19 @@ #include #include +#include #include #include #include -#include #include #include #include "drake/common/drake_assert.h" +#include "drake/common/drake_optional.h" #include "drake/common/drake_path.h" #include "drake/common/drake_throw.h" +#include "drake/common/filesystem.h" #include "drake/common/text_logging.h" namespace drake { @@ -32,7 +34,7 @@ PackageMap::PackageMap() {} void PackageMap::Add(const string& package_name, const string& package_path) { DRAKE_THROW_UNLESS(map_.count(package_name) == 0); - if (!spruce::path(package_path).exists()) { + if (!filesystem::is_directory(package_path)) { throw std::runtime_error( "Could not add package://" + package_name + " to the search path " "because directory " + package_path + " does not exist"); @@ -68,18 +70,20 @@ void PackageMap::PopulateFromEnvironment(const string& environment_variable) { namespace { -// Returns true if @p directory has a package.xml file. -bool HasPackageXmlFile(const string& directory) { +// Returns the package.xml file in the given directory, if any. +optional GetPackageXmlFile(const string& directory) { DRAKE_DEMAND(!directory.empty()); - spruce::path spruce_path(directory); - spruce_path.append("package.xml"); - return spruce_path.exists(); + filesystem::path filename = filesystem::path(directory) / "package.xml"; + if (filesystem::is_regular_file(filename)) { + return filename; + } + return nullopt; } // Returns the parent directory of @p directory. string GetParentDirectory(const string& directory) { DRAKE_DEMAND(!directory.empty()); - return spruce::path(directory).root(); + return filesystem::path(directory).parent_path().string(); } // Parses the package.xml file specified by package_xml_file. Finds and returns @@ -141,10 +145,8 @@ void PackageMap::PopulateUpstreamToDrakeHelper( } // If there is a new package.xml file, then add it. - if (HasPackageXmlFile(directory)) { - spruce::path spruce_path(directory); - spruce_path.append("package.xml"); - const string package_name = GetPackageName(spruce_path.getStr()); + if (auto filename = GetPackageXmlFile(directory)) { + const string package_name = GetPackageName(filename->string()); AddPackageIfNew(package_name, directory); } @@ -157,8 +159,7 @@ void PackageMap::PopulateUpstreamToDrake(const string& model_file) { DRAKE_DEMAND(!model_file.empty()); // Verify that the model_file names an URDF or SDF file. - spruce::path spruce_path(model_file); - string extension = spruce_path.extension(); + string extension = filesystem::path(model_file).extension().string(); std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); if (extension != ".urdf" && extension != ".sdf") { @@ -166,7 +167,7 @@ void PackageMap::PopulateUpstreamToDrake(const string& model_file) { "The file type '{}' is not supported for '{}'", extension, model_file)); } - const string model_dir = spruce_path.root(); + const string model_dir = filesystem::path(model_file).parent_path(); // Bail out if the model file is not part of Drake. const optional maybe_drake_path = MaybeGetDrakePath(); @@ -182,7 +183,7 @@ void PackageMap::PopulateUpstreamToDrake(const string& model_file) { } // Search the directory containing the model_file and "upstream". - PopulateUpstreamToDrakeHelper(spruce_path.root(), drake_path); + PopulateUpstreamToDrakeHelper(model_dir, drake_path); } void PackageMap::CrawlForPackages(const string& path) { @@ -217,9 +218,9 @@ void PackageMap::CrawlForPackages(const string& path) { CrawlForPackages(file.path); } else if (file.name == target_filename) { const string package_name = GetPackageName(file.path); - spruce::path spruce_path(file.path); - const string package_path = spruce_path.root().append("/"); - AddPackageIfNew(package_name, package_path); + const string package_path = + filesystem::path(file.path).parent_path().string(); + AddPackageIfNew(package_name, package_path + "/"); } tinydir_next(&dir); } diff --git a/multibody/parsing/parser.cc b/multibody/parsing/parser.cc index 42f63f98189d..114e6ee92570 100644 --- a/multibody/parsing/parser.cc +++ b/multibody/parsing/parser.cc @@ -1,7 +1,6 @@ #include "drake/multibody/parsing/parser.h" -#include - +#include "drake/common/filesystem.h" #include "drake/multibody/parsing/detail_sdf_parser.h" #include "drake/multibody/parsing/detail_urdf_parser.h" @@ -22,7 +21,7 @@ Parser::Parser( namespace { enum class FileType { kSdf, kUrdf }; FileType DetermineFileType(const std::string& file_name) { - const std::string ext = spruce::path(file_name).extension(); + const std::string ext = filesystem::path(file_name).extension().string(); if ((ext == ".urdf") || (ext == ".URDF")) { return FileType::kUrdf; } diff --git a/multibody/parsing/test/detail_path_utils_test.cc b/multibody/parsing/test/detail_path_utils_test.cc index 76d47f12bd00..6e6f52512f05 100644 --- a/multibody/parsing/test/detail_path_utils_test.cc +++ b/multibody/parsing/test/detail_path_utils_test.cc @@ -4,8 +4,8 @@ #include #include -#include +#include "drake/common/filesystem.h" #include "drake/common/find_resource.h" using std::string; @@ -28,8 +28,7 @@ GTEST_TEST(ParserPathUtilsTest, TestGetFullPath_Relative) { ASSERT_NO_THROW(full_path = GetFullPath(relative_path)); ASSERT_TRUE(!full_path.empty()); EXPECT_EQ(full_path[0], '/'); - spruce::path spruce_full_path(full_path); - EXPECT_TRUE(spruce_full_path.exists()); + EXPECT_TRUE(filesystem::exists({full_path})); // Absolute path unchanged. string full_path_idempotent; diff --git a/multibody/parsing/test/detail_sdf_parser_test.cc b/multibody/parsing/test/detail_sdf_parser_test.cc index 0e0c6ca80813..16cf639fde70 100644 --- a/multibody/parsing/test/detail_sdf_parser_test.cc +++ b/multibody/parsing/test/detail_sdf_parser_test.cc @@ -4,8 +4,8 @@ #include #include -#include +#include "drake/common/filesystem.h" #include "drake/common/find_resource.h" #include "drake/common/temp_directory.h" #include "drake/common/test_utilities/eigen_matrix_compare.h" @@ -43,13 +43,13 @@ GTEST_TEST(MultibodyPlantSdfParserTest, PackageMapSpecified) { const std::string full_sdf_filename = FindResourceOrThrow( "drake/multibody/parsing/test/box_package/sdfs/box.sdf"); - spruce::path package_path = full_sdf_filename; - package_path = package_path.root(); - package_path = package_path.root(); + filesystem::path package_path = full_sdf_filename; + package_path = package_path.parent_path(); + package_path = package_path.parent_path(); // Construct the PackageMap. PackageMap package_map; - package_map.PopulateFromFolder(package_path.getStr()); + package_map.PopulateFromFolder(package_path.string()); // Read in the SDF file. AddModelFromSdfFile(full_sdf_filename, "", package_map, &plant, &scene_graph); @@ -222,7 +222,7 @@ GTEST_TEST(SdfParser, IncludeTags) { const std::string full_name = FindResourceOrThrow( "drake/multibody/parsing/test/sdf_parser_test/" "include_models.sdf"); - sdf::addURIPath("model://", spruce::path(full_name).root()); + sdf::addURIPath("model://", filesystem::path(full_name).parent_path()); MultibodyPlant plant; // We start with the world and default model instances. diff --git a/multibody/parsing/test/detail_urdf_parser_test.cc b/multibody/parsing/test/detail_urdf_parser_test.cc index 26fdd183914d..e6dc361029b3 100644 --- a/multibody/parsing/test/detail_urdf_parser_test.cc +++ b/multibody/parsing/test/detail_urdf_parser_test.cc @@ -3,9 +3,9 @@ #include #include -#include #include "drake/common/eigen_types.h" +#include "drake/common/filesystem.h" #include "drake/common/find_resource.h" #include "drake/common/test_utilities/eigen_matrix_compare.h" #include "drake/multibody/parsing/detail_path_utils.h" @@ -29,13 +29,13 @@ GTEST_TEST(MultibodyPlantUrdfParserTest, PackageMapSpecified) { const std::string full_urdf_filename = FindResourceOrThrow( "drake/multibody/parsing/test/box_package/urdfs/box.urdf"); - spruce::path package_path = full_urdf_filename; - package_path = package_path.root(); - package_path = package_path.root(); + filesystem::path package_path = full_urdf_filename; + package_path = package_path.parent_path(); + package_path = package_path.parent_path(); // Construct the PackageMap. PackageMap package_map; - package_map.PopulateFromFolder(package_path.getStr()); + package_map.PopulateFromFolder(package_path.string()); // Read in the URDF file. AddModelFromUrdfFile(full_urdf_filename, "", package_map, &plant, diff --git a/multibody/parsing/test/package_map_test.cc b/multibody/parsing/test/package_map_test.cc index 8a72cb2fe10c..12f9138182a9 100644 --- a/multibody/parsing/test/package_map_test.cc +++ b/multibody/parsing/test/package_map_test.cc @@ -3,8 +3,8 @@ #include #include -#include +#include "drake/common/filesystem.h" #include "drake/common/find_resource.h" using std::map; @@ -39,8 +39,8 @@ void VerifyMatch(const PackageMap& package_map, // Tests that the PackageMap can be manually populated. GTEST_TEST(PackageMapTest, TestManualPopulation) { - spruce::dir::mkdir(spruce::path("package_foo")); - spruce::dir::mkdir(spruce::path("package_bar")); + filesystem::create_directory("package_foo"); + filesystem::create_directory("package_bar"); map expected_packages = { {"package_foo", "package_foo"}, {"my_package", "package_bar"} @@ -59,7 +59,7 @@ GTEST_TEST(PackageMapTest, TestPopulateFromXml) { const string xml_filename = FindResourceOrThrow( "drake/multibody/parsing/test/" "package_map_test_packages/package_map_test_package_a/package.xml"); - const string xml_dirname = spruce::path(xml_filename).root(); + const string xml_dirname = filesystem::path(xml_filename).parent_path(); PackageMap package_map; package_map.AddPackageXml(xml_filename); @@ -141,8 +141,8 @@ GTEST_TEST(PackageMapTest, TestPopulateUpstreamToDrake) { // Tests that PackageMap's streaming to-string operator works. GTEST_TEST(PackageMapTest, TestStreamingToString) { - spruce::dir::mkdir(spruce::path("package_foo")); - spruce::dir::mkdir(spruce::path("package_bar")); + filesystem::create_directory("package_foo"); + filesystem::create_directory("package_bar"); map expected_packages = { {"package_foo", "package_foo"}, {"my_package", "package_bar"} diff --git a/multibody/parsing/test/parser_test.cc b/multibody/parsing/test/parser_test.cc index e6e4ebc06db6..af58bbd9c6d5 100644 --- a/multibody/parsing/test/parser_test.cc +++ b/multibody/parsing/test/parser_test.cc @@ -1,8 +1,8 @@ #include "drake/multibody/parsing/parser.h" #include -#include +#include "drake/common/filesystem.h" #include "drake/common/find_resource.h" #include "drake/common/temp_directory.h" #include "drake/common/test_utilities/expect_throws_message.h" @@ -130,12 +130,11 @@ GTEST_TEST(FileParserTest, PackageMapTest) { // Copy the relevant files to the temporary directory. const std::string sdf_path = temp_dir + "/sdfs"; const std::string mesh_path = temp_dir + "/meshes"; - ASSERT_TRUE(spruce::dir::mkdir(sdf_path)); - ASSERT_TRUE(spruce::dir::mkdir(mesh_path)); - ASSERT_TRUE(spruce::file::copy(full_package_filename, - temp_dir + "/package.xml")); - ASSERT_TRUE(spruce::file::copy(full_sdf_filename, sdf_path + "/box.sdf")); - ASSERT_TRUE(spruce::file::copy(full_obj_filename, mesh_path + "/box.obj")); + filesystem::create_directory({sdf_path}); + filesystem::create_directory({mesh_path}); + filesystem::copy(full_package_filename, temp_dir + "/package.xml"); + filesystem::copy(full_sdf_filename, sdf_path + "/box.sdf"); + filesystem::copy(full_obj_filename, mesh_path + "/box.obj"); // Attempt to read in the SDF file without setting the package map first. const std::string new_sdf_filename = sdf_path + "/box.sdf";