moveit2
The MoveIt Motion Planning Framework for ROS 2.
create_deprecated_headers.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 
4 # Copyright 2024 Tom Noble.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions are met:
8 #
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 #
12 # * Redistributions in binary form must reproduce the above copyright
13 # notice, this list of conditions and the following disclaimer in the
14 # documentation and/or other materials provided with the distribution.
15 #
16 # * Neither the name of the copyright holder nor the names of its
17 # contributors may be used to endorse or promote products derived from
18 # this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
31 
32 # Author: Tom Noble
33 
34 import sys
35 import argparse
36 import logging
37 from typing import List, Tuple
38 from pathlib import Path
39 
40 
41 DISCLAIMER = """
42 /*********************************************************************
43  * All MoveIt 2 headers have been updated to use the .hpp extension.
44  *
45  * .h headers are now autogenerated via {},
46  * and will import the corresponding .hpp with a deprecation warning.
47  *
48  * imports via .h files may be removed in future releases, so please
49  * modify your imports to use the corresponding .hpp imports.
50  *
51  * See https://github.com/moveit/moveit2/pull/3113 for extra details.
52  *********************************************************************/
53 """
54 
55 
56 class NoIncludeGuard(Exception):
57  ERROR = "No include guard found in {}.hpp. Unable to generate pretext."
58 
59  def __init__(self, file: Path):
60  super().__init__(self.ERRORERROR.format(file))
61 
62 
63 class NoIncludeDirectory(Exception):
64  ERROR = "No include directory found for {}.hpp. Unable to generate relative .hpp include"
65 
66  def __init__(self, file: Path):
67  super().__init__(self.ERRORERROR.format(file))
68 
69 
70 class HppFile:
71  def __init__(self, path: Path):
72  self.pathpath = path
73  self.guardguard = "#pragma once"
74  self.pretextpretextpretext = self.pretextpretextpretext()
75  self.includeincludeinclude = self.includeincludeinclude()
76 
77  def drop_data_after(self, data: str, match: str):
78  return data[: data.find(match) + len(match)]
79 
80  def read(self) -> str:
81  data = open(self.pathpath, "r").read()
82  contains_guard = self.guardguard in data
83  if not contains_guard:
84  raise NoIncludeGuard(self.pathpath)
85  return data
86 
87  def pretext(self) -> str:
88  data = self.readread()
89  return self.drop_data_afterdrop_data_after(data, self.guardguard)
90 
91  def include(self) -> str:
92  ends_with_include = lambda p: str(p).endswith("include")
93  include_paths = [p for p in self.pathpath.parents if ends_with_include(p)]
94  if not include_paths:
95  raise NoIncludeDirectory(self.pathpath)
96  relative_import = self.pathpath.relative_to(include_paths[0])
97  return f"#include <{relative_import}>"
98 
99 
101  def __init__(self, hpp: HppFile):
102  self.hpphpp = hpp
103  self.pathpath = hpp.path.with_suffix(".h")
104  self.warnwarn = '#pragma message(".h header is obsolete. Please use the .hpp header instead.")'
105  self.contentscontentscontents = self.contentscontentscontents()
106 
107  def contents(self) -> str:
108  disclaimer = DISCLAIMER.format(Path(__file__).name).rstrip("\n")
109  items = [disclaimer, self.hpphpp.pretext, self.warnwarn, self.hpphpp.include]
110  return "\n".join(items) + "\n"
111 
112 
114  def __init__(self, n_processed_hpps: int, bad_hpps: List[str]):
115  self.n_processed_hppsn_processed_hpps = n_processed_hpps
116  self.bad_hppsbad_hpps = bad_hpps
117 
118  def were_all_hpps_processed(self) -> bool:
119  return len(self.bad_hppsbad_hpps) == 0
120 
121  def __repr__(self) -> str:
122  summary = f"Can generate {self.n_processed_hpps} .h files."
123  if self.bad_hppsbad_hpps:
124  summary += f" Cannot generate {len(self.bad_hpps)} .h files:\n\n"
125  summary += "\n".join([f"❌ {hpp}" for hpp in self.bad_hppsbad_hpps])
126  summary += "\n"
127  return summary
128 
129 
131  def __init__(self, hpp_paths: List[str]):
132  self.hpp_pathshpp_paths = hpp_paths
133  self.processed_hppsprocessed_hpps = []
134  self.bad_hppsbad_hpps = []
135 
136  def __process_hpp(self, hpp: str) -> None:
137  try:
138  self.processed_hppsprocessed_hpps.append(HppFile(hpp))
139  except (NoIncludeDirectory, NoIncludeGuard) as e:
140  self.bad_hppsbad_hpps.append(str(hpp))
141 
142  def process_all_hpps(self) -> HeaderSummary:
143  print(f"\nProcessing {len(self.hpp_paths)} .hpp files...")
144  _ = [self.__process_hpp__process_hpp(hpp) for hpp in self.hpp_pathshpp_paths]
145  return HeaderSummary(len(self.processed_hppsprocessed_hpps), self.bad_hppsbad_hpps)
146 
147  def create_h_files(self) -> None:
148  print(f"Proceeding to generate {len(self.processed_hpps)} .h files...")
149  h_files = [DeprecatedHeader(hpp) for hpp in self.processed_hppsprocessed_hpps]
150  _ = [open(h.path, "w").write(h.contents) for h in h_files]
151 
152 
153 if __name__ == "__main__":
154  parser = argparse.ArgumentParser()
155  # TODO: Add argument for skipping private headers
156  parser.add_argument("--apply", action="store_true", help="Generates the .h files")
157  args = parser.parse_args()
158  generator = DeprecatedHeaderGenerator(list(Path.cwd().rglob("*.hpp")))
159  summary = generator.process_all_hpps()
160  print(summary)
161  if args.apply and not summary.were_all_hpps_processed():
162  args.apply = input("Continue? (y/n): ").lower() == "y"
163  if args.apply:
164  generator.create_h_files()
165  else:
166  print("Skipping file generation...")
167  print("Done.\n")
def __init__(self, int n_processed_hpps, List[str] bad_hpps)
def drop_data_after(self, str data, str match)
std::string append(const std::string &left, const std::string &right)
KeyboardReader input
void print(PropagationDistanceField &pdf, int numX, int numY, int numZ)