diff --git a/CHANGELOG.md b/CHANGELOG.md index 83ec2ce..ce3ecae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,13 @@ generated in the following situations: * [Lint] Fix cascaded abstract types (#183) +* [TRLC] Add `--include-dir` as a long-form alias for `-I`, making + include path flags consistent and self-documenting. + +* [TRLC, API] Add `--include-file` CLI flag and `register_include_files()` + API method to add individual files to the include set without requiring + a full directory processing. + ### 2.0.4 * [TRLC_RST] Add tool to convert TRLC Requirements to Sphinx RST Files diff --git a/documentation/TUTORIAL-PACKAGE.md b/documentation/TUTORIAL-PACKAGE.md index c01b4e6..7d6b919 100644 --- a/documentation/TUTORIAL-PACKAGE.md +++ b/documentation/TUTORIAL-PACKAGE.md @@ -119,16 +119,27 @@ Often when running TRLC you are not really interested in the entire repository, but only one or two files. **If** you have sectioned everything nicely with packages (i.e. not -just have one giganting package with 100k requirements) then TRLC -offers an alternative way of running it with the `-I` switch. +just have one gigantic package with 100k requirements) then TRLC +offers an alternative way of running it with the `--include-dir` switch. For example: ```bash -$ trlc -I global myfile.trlc +$ trlc --include-dir global myfile.trlc ``` Would discover trlc files in `global` and analyse their dependencies and then only pull in the files that are required to understand `myfile.trlc`. This can massively improve performance, if the interconnections between the packages are not too dense. + +`-I` is an older alternative to the `--include-dir` flag. + +If the exact file paths needed to run TRLC are already identified, +we can use the `--include-file` switch to mention their paths directly. + +For example: + +```bash +$ trlc --include-file global/foo.rsl --include-file dep.trlc myfile.trlc +``` diff --git a/tests-large-partial/partial-3/options b/tests-large-partial/partial-3/options new file mode 100644 index 0000000..54a7019 --- /dev/null +++ b/tests-large-partial/partial-3/options @@ -0,0 +1 @@ +--verify --show-file-list --include-dir large large/package_1_1.trlc diff --git a/tests-large-partial/partial-3/output b/tests-large-partial/partial-3/output new file mode 100644 index 0000000..4b01d1d --- /dev/null +++ b/tests-large-partial/partial-3/output @@ -0,0 +1,210 @@ +Processed 2 (of 50) models and 5 (of 159) requirement files and found no issues +> [Included] Model large/generic.rsl (Package generic) +> [Included] Model large/package_1.rsl (Package package_1) +> [Excluded] Model large/package_10.rsl (Package package_10) +> [Excluded] Model large/package_11.rsl (Package package_11) +> [Excluded] Model large/package_12.rsl (Package package_12) +> [Excluded] Model large/package_13.rsl (Package package_13) +> [Excluded] Model large/package_14.rsl (Package package_14) +> [Excluded] Model large/package_15.rsl (Package package_15) +> [Excluded] Model large/package_16.rsl (Package package_16) +> [Excluded] Model large/package_17.rsl (Package package_17) +> [Excluded] Model large/package_18.rsl (Package package_18) +> [Excluded] Model large/package_19.rsl (Package package_19) +> [Excluded] Model large/package_2.rsl (Package package_2) +> [Excluded] Model large/package_20.rsl (Package package_20) +> [Excluded] Model large/package_21.rsl (Package package_21) +> [Excluded] Model large/package_22.rsl (Package package_22) +> [Excluded] Model large/package_23.rsl (Package package_23) +> [Excluded] Model large/package_24.rsl (Package package_24) +> [Excluded] Model large/package_25.rsl (Package package_25) +> [Excluded] Model large/package_26.rsl (Package package_26) +> [Excluded] Model large/package_27.rsl (Package package_27) +> [Excluded] Model large/package_28.rsl (Package package_28) +> [Excluded] Model large/package_29.rsl (Package package_29) +> [Excluded] Model large/package_3.rsl (Package package_3) +> [Excluded] Model large/package_30.rsl (Package package_30) +> [Excluded] Model large/package_31.rsl (Package package_31) +> [Excluded] Model large/package_32.rsl (Package package_32) +> [Excluded] Model large/package_33.rsl (Package package_33) +> [Excluded] Model large/package_34.rsl (Package package_34) +> [Excluded] Model large/package_35.rsl (Package package_35) +> [Excluded] Model large/package_36.rsl (Package package_36) +> [Excluded] Model large/package_37.rsl (Package package_37) +> [Excluded] Model large/package_38.rsl (Package package_38) +> [Excluded] Model large/package_39.rsl (Package package_39) +> [Excluded] Model large/package_4.rsl (Package package_4) +> [Excluded] Model large/package_40.rsl (Package package_40) +> [Excluded] Model large/package_41.rsl (Package package_41) +> [Excluded] Model large/package_42.rsl (Package package_42) +> [Excluded] Model large/package_43.rsl (Package package_43) +> [Excluded] Model large/package_44.rsl (Package package_44) +> [Excluded] Model large/package_45.rsl (Package package_45) +> [Excluded] Model large/package_46.rsl (Package package_46) +> [Excluded] Model large/package_47.rsl (Package package_47) +> [Excluded] Model large/package_48.rsl (Package package_48) +> [Excluded] Model large/package_49.rsl (Package package_49) +> [Excluded] Model large/package_5.rsl (Package package_5) +> [Excluded] Model large/package_6.rsl (Package package_6) +> [Excluded] Model large/package_7.rsl (Package package_7) +> [Excluded] Model large/package_8.rsl (Package package_8) +> [Excluded] Model large/package_9.rsl (Package package_9) +> [Excluded] Requirements large/package_10_1.trlc (Package package_10) +> [Excluded] Requirements large/package_11_1.trlc (Package package_11) +> [Excluded] Requirements large/package_11_2.trlc (Package package_11) +> [Excluded] Requirements large/package_11_3.trlc (Package package_11) +> [Excluded] Requirements large/package_12_1.trlc (Package package_12) +> [Excluded] Requirements large/package_12_2.trlc (Package package_12) +> [Excluded] Requirements large/package_12_3.trlc (Package package_12) +> [Excluded] Requirements large/package_13_1.trlc (Package package_13) +> [Excluded] Requirements large/package_13_2.trlc (Package package_13) +> [Excluded] Requirements large/package_14_1.trlc (Package package_14) +> [Excluded] Requirements large/package_14_2.trlc (Package package_14) +> [Excluded] Requirements large/package_14_3.trlc (Package package_14) +> [Excluded] Requirements large/package_14_4.trlc (Package package_14) +> [Excluded] Requirements large/package_15_1.trlc (Package package_15) +> [Excluded] Requirements large/package_15_2.trlc (Package package_15) +> [Excluded] Requirements large/package_16_1.trlc (Package package_16) +> [Excluded] Requirements large/package_16_2.trlc (Package package_16) +> [Excluded] Requirements large/package_16_3.trlc (Package package_16) +> [Excluded] Requirements large/package_16_4.trlc (Package package_16) +> [Excluded] Requirements large/package_17_1.trlc (Package package_17) +> [Excluded] Requirements large/package_18_1.trlc (Package package_18) +> [Excluded] Requirements large/package_19_1.trlc (Package package_19) +> [Excluded] Requirements large/package_19_2.trlc (Package package_19) +> [Excluded] Requirements large/package_19_3.trlc (Package package_19) +> [Excluded] Requirements large/package_19_4.trlc (Package package_19) +> [Primary] Requirements large/package_1_1.trlc (Package package_1) +> [Included] Requirements large/package_1_2.trlc (Package package_1) +> [Included] Requirements large/package_1_3.trlc (Package package_1) +> [Included] Requirements large/package_1_4.trlc (Package package_1) +> [Included] Requirements large/package_1_5.trlc (Package package_1) +> [Excluded] Requirements large/package_20_1.trlc (Package package_20) +> [Excluded] Requirements large/package_20_2.trlc (Package package_20) +> [Excluded] Requirements large/package_21_1.trlc (Package package_21) +> [Excluded] Requirements large/package_21_2.trlc (Package package_21) +> [Excluded] Requirements large/package_21_3.trlc (Package package_21) +> [Excluded] Requirements large/package_21_4.trlc (Package package_21) +> [Excluded] Requirements large/package_22_1.trlc (Package package_22) +> [Excluded] Requirements large/package_22_2.trlc (Package package_22) +> [Excluded] Requirements large/package_22_3.trlc (Package package_22) +> [Excluded] Requirements large/package_22_4.trlc (Package package_22) +> [Excluded] Requirements large/package_23_1.trlc (Package package_23) +> [Excluded] Requirements large/package_23_2.trlc (Package package_23) +> [Excluded] Requirements large/package_23_3.trlc (Package package_23) +> [Excluded] Requirements large/package_23_4.trlc (Package package_23) +> [Excluded] Requirements large/package_23_5.trlc (Package package_23) +> [Excluded] Requirements large/package_24_1.trlc (Package package_24) +> [Excluded] Requirements large/package_24_2.trlc (Package package_24) +> [Excluded] Requirements large/package_25_1.trlc (Package package_25) +> [Excluded] Requirements large/package_25_2.trlc (Package package_25) +> [Excluded] Requirements large/package_25_3.trlc (Package package_25) +> [Excluded] Requirements large/package_25_4.trlc (Package package_25) +> [Excluded] Requirements large/package_26_1.trlc (Package package_26) +> [Excluded] Requirements large/package_26_2.trlc (Package package_26) +> [Excluded] Requirements large/package_27_1.trlc (Package package_27) +> [Excluded] Requirements large/package_27_2.trlc (Package package_27) +> [Excluded] Requirements large/package_27_3.trlc (Package package_27) +> [Excluded] Requirements large/package_27_4.trlc (Package package_27) +> [Excluded] Requirements large/package_28_1.trlc (Package package_28) +> [Excluded] Requirements large/package_28_2.trlc (Package package_28) +> [Excluded] Requirements large/package_28_3.trlc (Package package_28) +> [Excluded] Requirements large/package_28_4.trlc (Package package_28) +> [Excluded] Requirements large/package_29_1.trlc (Package package_29) +> [Excluded] Requirements large/package_29_2.trlc (Package package_29) +> [Excluded] Requirements large/package_29_3.trlc (Package package_29) +> [Excluded] Requirements large/package_2_1.trlc (Package package_2) +> [Excluded] Requirements large/package_2_2.trlc (Package package_2) +> [Excluded] Requirements large/package_2_3.trlc (Package package_2) +> [Excluded] Requirements large/package_2_4.trlc (Package package_2) +> [Excluded] Requirements large/package_2_5.trlc (Package package_2) +> [Excluded] Requirements large/package_30_1.trlc (Package package_30) +> [Excluded] Requirements large/package_30_2.trlc (Package package_30) +> [Excluded] Requirements large/package_30_3.trlc (Package package_30) +> [Excluded] Requirements large/package_30_4.trlc (Package package_30) +> [Excluded] Requirements large/package_30_5.trlc (Package package_30) +> [Excluded] Requirements large/package_31_1.trlc (Package package_31) +> [Excluded] Requirements large/package_31_2.trlc (Package package_31) +> [Excluded] Requirements large/package_31_3.trlc (Package package_31) +> [Excluded] Requirements large/package_31_4.trlc (Package package_31) +> [Excluded] Requirements large/package_31_5.trlc (Package package_31) +> [Excluded] Requirements large/package_32_1.trlc (Package package_32) +> [Excluded] Requirements large/package_32_2.trlc (Package package_32) +> [Excluded] Requirements large/package_33_1.trlc (Package package_33) +> [Excluded] Requirements large/package_33_2.trlc (Package package_33) +> [Excluded] Requirements large/package_34_1.trlc (Package package_34) +> [Excluded] Requirements large/package_34_2.trlc (Package package_34) +> [Excluded] Requirements large/package_34_3.trlc (Package package_34) +> [Excluded] Requirements large/package_34_4.trlc (Package package_34) +> [Excluded] Requirements large/package_34_5.trlc (Package package_34) +> [Excluded] Requirements large/package_35_1.trlc (Package package_35) +> [Excluded] Requirements large/package_36_1.trlc (Package package_36) +> [Excluded] Requirements large/package_36_2.trlc (Package package_36) +> [Excluded] Requirements large/package_36_3.trlc (Package package_36) +> [Excluded] Requirements large/package_36_4.trlc (Package package_36) +> [Excluded] Requirements large/package_36_5.trlc (Package package_36) +> [Excluded] Requirements large/package_37_1.trlc (Package package_37) +> [Excluded] Requirements large/package_37_2.trlc (Package package_37) +> [Excluded] Requirements large/package_37_3.trlc (Package package_37) +> [Excluded] Requirements large/package_38_1.trlc (Package package_38) +> [Excluded] Requirements large/package_38_2.trlc (Package package_38) +> [Excluded] Requirements large/package_39_1.trlc (Package package_39) +> [Excluded] Requirements large/package_39_2.trlc (Package package_39) +> [Excluded] Requirements large/package_39_3.trlc (Package package_39) +> [Excluded] Requirements large/package_39_4.trlc (Package package_39) +> [Excluded] Requirements large/package_3_1.trlc (Package package_3) +> [Excluded] Requirements large/package_3_2.trlc (Package package_3) +> [Excluded] Requirements large/package_3_3.trlc (Package package_3) +> [Excluded] Requirements large/package_40_1.trlc (Package package_40) +> [Excluded] Requirements large/package_40_2.trlc (Package package_40) +> [Excluded] Requirements large/package_40_3.trlc (Package package_40) +> [Excluded] Requirements large/package_40_4.trlc (Package package_40) +> [Excluded] Requirements large/package_41_1.trlc (Package package_41) +> [Excluded] Requirements large/package_41_2.trlc (Package package_41) +> [Excluded] Requirements large/package_41_3.trlc (Package package_41) +> [Excluded] Requirements large/package_41_4.trlc (Package package_41) +> [Excluded] Requirements large/package_41_5.trlc (Package package_41) +> [Excluded] Requirements large/package_42_1.trlc (Package package_42) +> [Excluded] Requirements large/package_43_1.trlc (Package package_43) +> [Excluded] Requirements large/package_43_2.trlc (Package package_43) +> [Excluded] Requirements large/package_43_3.trlc (Package package_43) +> [Excluded] Requirements large/package_44_1.trlc (Package package_44) +> [Excluded] Requirements large/package_44_2.trlc (Package package_44) +> [Excluded] Requirements large/package_44_3.trlc (Package package_44) +> [Excluded] Requirements large/package_44_4.trlc (Package package_44) +> [Excluded] Requirements large/package_44_5.trlc (Package package_44) +> [Excluded] Requirements large/package_45_1.trlc (Package package_45) +> [Excluded] Requirements large/package_45_2.trlc (Package package_45) +> [Excluded] Requirements large/package_45_3.trlc (Package package_45) +> [Excluded] Requirements large/package_45_4.trlc (Package package_45) +> [Excluded] Requirements large/package_45_5.trlc (Package package_45) +> [Excluded] Requirements large/package_46_1.trlc (Package package_46) +> [Excluded] Requirements large/package_47_1.trlc (Package package_47) +> [Excluded] Requirements large/package_47_2.trlc (Package package_47) +> [Excluded] Requirements large/package_47_3.trlc (Package package_47) +> [Excluded] Requirements large/package_47_4.trlc (Package package_47) +> [Excluded] Requirements large/package_47_5.trlc (Package package_47) +> [Excluded] Requirements large/package_48_1.trlc (Package package_48) +> [Excluded] Requirements large/package_48_2.trlc (Package package_48) +> [Excluded] Requirements large/package_48_3.trlc (Package package_48) +> [Excluded] Requirements large/package_49_1.trlc (Package package_49) +> [Excluded] Requirements large/package_49_2.trlc (Package package_49) +> [Excluded] Requirements large/package_49_3.trlc (Package package_49) +> [Excluded] Requirements large/package_4_1.trlc (Package package_4) +> [Excluded] Requirements large/package_4_2.trlc (Package package_4) +> [Excluded] Requirements large/package_4_3.trlc (Package package_4) +> [Excluded] Requirements large/package_4_4.trlc (Package package_4) +> [Excluded] Requirements large/package_4_5.trlc (Package package_4) +> [Excluded] Requirements large/package_5_1.trlc (Package package_5) +> [Excluded] Requirements large/package_5_2.trlc (Package package_5) +> [Excluded] Requirements large/package_5_3.trlc (Package package_5) +> [Excluded] Requirements large/package_5_4.trlc (Package package_5) +> [Excluded] Requirements large/package_6_1.trlc (Package package_6) +> [Excluded] Requirements large/package_6_2.trlc (Package package_6) +> [Excluded] Requirements large/package_7_1.trlc (Package package_7) +> [Excluded] Requirements large/package_8_1.trlc (Package package_8) +> [Excluded] Requirements large/package_8_2.trlc (Package package_8) +> [Excluded] Requirements large/package_8_3.trlc (Package package_8) +> [Excluded] Requirements large/package_8_4.trlc (Package package_8) +> [Excluded] Requirements large/package_8_5.trlc (Package package_8) +> [Excluded] Requirements large/package_9_1.trlc (Package package_9) diff --git a/tests-large-partial/partial-4/options b/tests-large-partial/partial-4/options new file mode 100644 index 0000000..5a96108 --- /dev/null +++ b/tests-large-partial/partial-4/options @@ -0,0 +1 @@ +--verify --show-file-list --include-file ../tests-system/simple/foo.rsl --include-file ../tests-system/simple/bar.rsl ../tests-system/simple/instances.trlc \ No newline at end of file diff --git a/tests-large-partial/partial-4/output b/tests-large-partial/partial-4/output new file mode 100644 index 0000000..3ca2e38 --- /dev/null +++ b/tests-large-partial/partial-4/output @@ -0,0 +1,4 @@ +Processed 2 models and 1 requirement file and found no issues +> [Included] Model ../tests-system/simple/bar.rsl (Package bar) +> [Included] Model ../tests-system/simple/foo.rsl (Package foo) +> [Primary] Requirements ../tests-system/simple/instances.trlc (Package instances) diff --git a/tests-unit/BUILD.bazel b/tests-unit/BUILD.bazel index c3f6ab4..cf93890 100644 --- a/tests-unit/BUILD.bazel +++ b/tests-unit/BUILD.bazel @@ -18,11 +18,18 @@ py_test( deps = ["//trlc:trlc"], ) +py_test( + name = "test_source_manager", + srcs = ["test_source_manager.py"], + deps = ["//trlc:trlc"], +) + test_suite( name = "unit_tests", tests = [ ":test_ast_bysection", ":test_lexer", ":test_lexer_base", + ":test_source_manager", ], ) diff --git a/tests-unit/test_source_manager.py b/tests-unit/test_source_manager.py new file mode 100644 index 0000000..4968873 --- /dev/null +++ b/tests-unit/test_source_manager.py @@ -0,0 +1,82 @@ +import os +import tempfile +import unittest + +from trlc.errors import Message_Handler +from trlc.trlc import Source_Manager + + +class Test_Register_Include_Files(unittest.TestCase): + def setUp(self): + self.mh = Message_Handler() + self.sm = Source_Manager(self.mh) + + def testNotAListRaisesTypeError(self): + with self.assertRaises(TypeError) as ctx: + self.sm.register_include_files("not_a_list.rsl") + self.assertIn("list", str(ctx.exception)) + + def testItemNotStringRaisesTypeError(self): + with self.assertRaises(TypeError) as ctx: + self.sm.register_include_files([42]) + self.assertIn("string", str(ctx.exception)) + + def testMissingFileRaisesFileNotFound(self): + with self.assertRaises(FileNotFoundError) as ctx: + self.sm.register_include_files(["/does/not/exist.rsl"]) + self.assertIn("does not exist", str(ctx.exception).lower()) + + def testNonRslTrlcFileIsIgnored(self): + with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as f: + txt_path = f.name + try: + self.sm.register_include_files([txt_path]) + self.assertNotIn(os.path.abspath(txt_path), self.sm.includes) + finally: + os.unlink(txt_path) + + def testRslFileIsRegistered(self): + with tempfile.NamedTemporaryFile(suffix=".rsl", delete=False) as f: + rsl_path = f.name + try: + self.sm.register_include_files([rsl_path]) + self.assertIn(os.path.abspath(rsl_path), self.sm.includes) + finally: + os.unlink(rsl_path) + + def testTrlcFileIsRegistered(self): + with tempfile.NamedTemporaryFile(suffix=".trlc", delete=False) as f: + trlc_path = f.name + try: + self.sm.register_include_files([trlc_path]) + self.assertIn(os.path.abspath(trlc_path), self.sm.includes) + finally: + os.unlink(trlc_path) + + def testEmptyListIsValid(self): + self.sm.register_include_files([]) + self.assertEqual(self.sm.includes, {}) + + def testMultipleFilesAllRegistered(self): + paths = [] + try: + for suffix in (".rsl", ".trlc"): + f = tempfile.NamedTemporaryFile(suffix=suffix, delete=False) + f.close() + paths.append(f.name) + self.sm.register_include_files(paths) + for p in paths: + self.assertIn(os.path.abspath(p), self.sm.includes) + finally: + for p in paths: + os.unlink(p) + + def testStopsAtFirstMissingFile(self): + with tempfile.NamedTemporaryFile(suffix=".rsl", delete=False) as f: + rsl_path = f.name + try: + with self.assertRaises(FileNotFoundError): + self.sm.register_include_files([rsl_path, "/no/such/file.rsl"]) + self.assertIn(os.path.abspath(rsl_path), self.sm.includes) + finally: + os.unlink(rsl_path) diff --git a/trlc/trlc.py b/trlc/trlc.py index f4d64c6..df73e2f 100644 --- a/trlc/trlc.py +++ b/trlc/trlc.py @@ -195,6 +195,35 @@ def register_include(self, dir_name): if os.path.splitext(file_name)[1] in (".rsl", ".trlc"))}) + def register_include_files(self, file_names): + """Make a list of files available for automatic inclusion + + :param file_names: list of files for automatic inclusion + :type file_names: list[str] + :raises TypeError: if file_names is not a list + :raises TypeError: if any item in file_names is not a string + :raises FileNotFoundError: if any file does not exist + """ + + if not isinstance(file_names, list): + raise TypeError( + f"file_names must be a list, got {type(file_names).__name__}" + ) + + for file_name in file_names: + if not isinstance(file_name, str): + raise TypeError( + f"file_name must be a string, got {type(file_name).__name__}" + ) + + if not os.path.isfile(file_name): + raise FileNotFoundError( + f"File does not exist: {file_name}" + ) + + if os.path.splitext(file_name)[1] in (".rsl", ".trlc"): + self.includes[os.path.abspath(file_name)] = file_name + def register_file(self, file_name, file_content=None, primary=True): """Schedule a file for parsing. @@ -590,13 +619,22 @@ def trlc(): action="store_true", help=("Enter bazel-* directories, which are" " excluded by default.")) - og_input.add_argument("-I", + og_input.add_argument("-I", "--include-dir", action="append", dest="include_dirs", + metavar="DIR", help=("Add include path. Files from these" " directories are parsed only when needed." " Can be specified more than once."), default=[]) + og_input.add_argument("--include-file", + action="append", + dest="include_files", + metavar="FILE", + help=("Add a single file to the include set." + " The file is parsed only when needed." + " Can be specified more than once."), + default=[]) og_output = ap.add_argument_group("output options") og_output.add_argument("--version", @@ -729,6 +767,12 @@ def trlc(): for path_name in options.include_dirs: sm.register_include(path_name) + for file_name in options.include_files: + if not os.path.isfile(file_name): + ap.error("include file %s is not a file" % file_name) + if options.include_files: + sm.register_include_files(options.include_files) + # Process input files, defaulting to the current directory if none # given. for path_name in options.items: