Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add package resource related utility functions to cpp API #27

Merged
merged 2 commits into from
Nov 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion ament_index_cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ endif()
find_package(ament_cmake REQUIRED)

add_library(${PROJECT_NAME} SHARED
src/get_package_prefix.cpp
src/get_package_share_directory.cpp
src/get_packages_with_prefixes.cpp
src/get_resource.cpp
src/get_resources.cpp
src/get_search_paths.cpp
src/has_resource.cpp)
src/has_resource.cpp
)
target_compile_definitions(${PROJECT_NAME} PRIVATE "AMENT_INDEX_CPP_BUILDING_DLL")
target_include_directories(${PROJECT_NAME} PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
Expand Down
51 changes: 51 additions & 0 deletions ament_index_cpp/include/ament_index_cpp/get_package_prefix.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2017 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef AMENT_INDEX_CPP__GET_PACKAGE_PREFIX_HPP_
#define AMENT_INDEX_CPP__GET_PACKAGE_PREFIX_HPP_

#include <stdexcept>
#include <string>

#include "ament_index_cpp/visibility_control.h"

namespace ament_index_cpp
{

/// Thrown when a package is not found.
class PackageNotFoundError : public std::out_of_range
{
public:
AMENT_INDEX_CPP_PUBLIC
explicit PackageNotFoundError(const std::string & package_name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the package name be stored as a member in the custom exception class?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 sure: 0d43992


AMENT_INDEX_CPP_PUBLIC
virtual ~PackageNotFoundError();

const std::string package_name;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why should the member be const? For a non-const instance of this class the user might want to change this member.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the package name is baked into the error message, and if you change this then it would be inconsistent with the what(), so you should only set it from the constructor. If you want a different package name in the error, I'd say create another object.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't think that the class should prescribe the member to be const.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately I don't agree. I've given a reason why it would be undesirable to change the package name after creation, but I haven't seen a reason why you would want to change it. I'm willing to change it if a convincing reason was given and that outweighs the downside I've already stated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The package for which the exception was thrown does not seem like it should be modifiable after the fact or in the traceback/error handling thus keeping it const would make sense. And relaxing the const constraint in the future will always be easier than adding it if there is a use case that needs it than the reverse.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, I looked at making a setter and getter for the package_name, as a compromise, but that would require me to override the what() method (storage for what() is not exposed in the exception class) and provide my own storage which would be redundant. Also the what() function is noexcept:

http://en.cppreference.com/w/cpp/error/exception/what

So, the creating of the new std::string storage would need to be done in the setter, more or less what the constructor is doing. So I don't think it would be any easier (more efficient) than creating a new exception if you needed to change the package name (still don't know why you would need to do so).

};

/// Return the installation prefix of the given package if found.
/**
* \param package_name the name of the package to locate.
* \return installation prefix path in which the package was found.
* \throws PackageNotFoundError when the given package is not found.
*/
AMENT_INDEX_CPP_PUBLIC
std::string
get_package_prefix(const std::string & package_name);

} // namespace ament_index_cpp

#endif // AMENT_INDEX_CPP__GET_PACKAGE_PREFIX_HPP_
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2017 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef AMENT_INDEX_CPP__GET_PACKAGE_SHARE_DIRECTORY_HPP_
#define AMENT_INDEX_CPP__GET_PACKAGE_SHARE_DIRECTORY_HPP_

#include <string>

#include "ament_index_cpp/visibility_control.h"

namespace ament_index_cpp
{

/// Return the share directory of the given package if found.
/**
* \param package_name the name of the package to locate.
* \return share path of the package.
* \throws PackageNotFoundError when the given package is not found.
*/
AMENT_INDEX_CPP_PUBLIC
std::string
get_package_share_directory(const std::string & package_name);

} // namespace ament_index_cpp

#endif // AMENT_INDEX_CPP__GET_PACKAGE_SHARE_DIRECTORY_HPP_
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2017 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef AMENT_INDEX_CPP__GET_PACKAGES_WITH_PREFIXES_HPP_
#define AMENT_INDEX_CPP__GET_PACKAGES_WITH_PREFIXES_HPP_

#include <map>
#include <string>

#include "ament_index_cpp/visibility_control.h"

namespace ament_index_cpp
{

/// Return a map of package names to their installation prefix.
AMENT_INDEX_CPP_PUBLIC
std::map<std::string, std::string>
get_packages_with_prefixes();

} // namespace ament_index_cpp

#endif // AMENT_INDEX_CPP__GET_PACKAGES_WITH_PREFIXES_HPP_
59 changes: 59 additions & 0 deletions ament_index_cpp/src/get_package_prefix.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2017 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "ament_index_cpp/get_package_prefix.hpp"

#include <stdexcept>
#include <string>

#include "ament_index_cpp/get_resource.hpp"
#include "ament_index_cpp/get_search_paths.hpp"

namespace ament_index_cpp
{

static
std::string
format_package_not_found_error_message(const std::string & package_name)
{
std::string message = "package '" + package_name + "' not found, searching: [";
auto search_paths = get_search_paths();
for (const auto & path : search_paths) {
message += path + ", ";
}
if (search_paths.size() > 0) {
message = message.substr(0, message.size() - 2);
}
return message + "]";
}

PackageNotFoundError::PackageNotFoundError(const std::string & _package_name)
: std::out_of_range(format_package_not_found_error_message(_package_name)),
package_name(_package_name)
{}

PackageNotFoundError::~PackageNotFoundError() {}

std::string
get_package_prefix(const std::string & package_name)
{
std::string content;
std::string prefix_path;
if (!get_resource("packages", package_name, content, &prefix_path)) {
throw PackageNotFoundError(package_name);
}
return prefix_path;
}

} // namespace ament_index_cpp
30 changes: 30 additions & 0 deletions ament_index_cpp/src/get_package_share_directory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2017 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "ament_index_cpp/get_package_share_directory.hpp"

#include <string>

#include "ament_index_cpp/get_package_prefix.hpp"

namespace ament_index_cpp
{

std::string
get_package_share_directory(const std::string & package_name)
{
return get_package_prefix(package_name) + "/share/" + package_name;
}

} // namespace ament_index_cpp
31 changes: 31 additions & 0 deletions ament_index_cpp/src/get_packages_with_prefixes.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2017 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "ament_index_cpp/get_packages_with_prefixes.hpp"

#include <map>
#include <string>

#include "ament_index_cpp/get_resources.hpp"

namespace ament_index_cpp
{

std::map<std::string, std::string>
get_packages_with_prefixes()
{
return get_resources("packages");
}

} // namespace ament_index_cpp
56 changes: 56 additions & 0 deletions ament_index_cpp/test/utest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
#include <stdexcept>
#include <string>

#include "ament_index_cpp/get_package_prefix.hpp"
#include "ament_index_cpp/get_package_share_directory.hpp"
#include "ament_index_cpp/get_packages_with_prefixes.hpp"
#include "ament_index_cpp/get_resource.hpp"
#include "ament_index_cpp/get_resources.hpp"
#include "ament_index_cpp/get_search_paths.hpp"
Expand Down Expand Up @@ -210,3 +213,56 @@ TEST(AmentIndexCpp, get_resource_underlay_base_path) {
EXPECT_TRUE(success);
EXPECT_EQ(base_path, generate_subfolder_path("prefix2"));
}

TEST(AmentIndexCpp, get_package_prefix) {
// Ensure that a known to exist package is found and that a known to not exist package is not.
std::list<std::string> subfolders;
subfolders.push_back("prefix1"); // only contains foo and bar packages
subfolders.push_back("prefix2"); // only contains bar and baz packages
set_ament_prefix_path(subfolders);
// foo is found in prefix 1
EXPECT_EQ(generate_subfolder_path("prefix1"), ament_index_cpp::get_package_prefix("foo"));
// bar is in both, but prefix 1 takes precedence
EXPECT_EQ(generate_subfolder_path("prefix1"), ament_index_cpp::get_package_prefix("bar"));
// baz is found in prefix 2 only
EXPECT_EQ(generate_subfolder_path("prefix2"), ament_index_cpp::get_package_prefix("baz"));
// exception when package is not found
EXPECT_THROW(
ament_index_cpp::get_package_prefix("does_not_exist"),
ament_index_cpp::PackageNotFoundError);
// exception when the package name is empty
EXPECT_THROW(
ament_index_cpp::get_package_prefix(""),
std::runtime_error);
}

TEST(AmentIndexCpp, get_package_share_directory) {
// Ensure that a known to exist package is found and the share directory is correct.
std::list<std::string> subfolders;
subfolders.push_back("prefix1"); // only contains foo and bar packages
subfolders.push_back("prefix2"); // only contains bar and baz packages
set_ament_prefix_path(subfolders);
// bar is in both, but prefix 1 takes precedence
EXPECT_EQ(
generate_subfolder_path("prefix1") + "/share/bar",
ament_index_cpp::get_package_share_directory("bar"));
}

TEST(AmentIndexCpp, get_packages_with_prefixes) {
// Ensure the list of packages and prefixes matches resource layout.
std::list<std::string> subfolders;
subfolders.push_back("prefix1"); // only contains foo and bar packages
subfolders.push_back("prefix2"); // only contains bar and baz packages
set_ament_prefix_path(subfolders);

std::map<std::string, std::string> packages_with_prefixes =
ament_index_cpp::get_packages_with_prefixes();
// should be exactly three elements
EXPECT_EQ(3UL, packages_with_prefixes.size());
// foo is found in prefix 1
EXPECT_EQ(generate_subfolder_path("prefix1"), packages_with_prefixes["foo"]);
// bar is in both, but prefix 1 takes precedence
EXPECT_EQ(generate_subfolder_path("prefix1"), packages_with_prefixes["bar"]);
// baz is found in prefix 2 only
EXPECT_EQ(generate_subfolder_path("prefix2"), packages_with_prefixes["baz"]);
}