Coverage for apio/commands/apio_format.py: 74%
61 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-24 03:51 +0000
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-24 03:51 +0000
1# -*- coding: utf-8 -*-
2# -- This file is part of the Apio project
3# -- (C) 2016-2024 FPGAwars
4# -- Authors
5# -- * Jesús Arroyo (2016-2019)
6# -- * Juan Gonzalez (obijuan) (2019-2024)
7# -- License GPLv2
8"""Implementation of 'apio format' command"""
10import sys
11import os
12from pathlib import Path
13from glob import glob
14from typing import Tuple, List, Optional
15import click
16from apio.common.apio_console import cout, cerror, cstyle
17from apio.common.apio_styles import EMPH3, SUCCESS, INFO, ERROR
18from apio.common.common_util import PROJECT_BUILD_PATH, sort_files
19from apio.apio_context import (
20 ApioContext,
21 PackagesPolicy,
22 ProjectPolicy,
23 RemoteConfigPolicy,
24)
25from apio.commands import options
26from apio.utils import util, cmd_util
29# -------------- apio format
31# -- Text in the rich-text format of the python rich library.
32APIO_FORMAT_HELP = """
33The command 'apio format' formats the project's source files to ensure \
34consistency and style without altering their semantics. The command accepts \
35the names of specific source files to format or formats all project source \
36files by default.
38Examples:[code]
39 apio format # Format all source files.
40 apio format -v # Same but with verbose output.
41 apio format main.v main_tb.v # Format the two files.[/code]
43[NOTE] The file arguments are relative to the project directory, even if \
44the --project-dir option is used.
46The format command utilizes the format tool from the Verible project, which \
47can be configured by setting its flags in the apio.ini project file \
48For example:
51[code]format-verible-options =
52 --column_limit=80
53 --indentation_spaces=4[/code]
55If needed, sections of source code can be protected from formatting using \
56Verible formatter directives:
58[code]// verilog_format: off
59... untouched code ...
60// verilog_format: on[/code]
62For a full list of Verible formatter flags, refer to the documentation page \
63online or use the command 'apio raw -- verible-verilog-format --helpfull'.
64"""
66# -- File types that the format support. 'sv' indicates System Verilog
67# -- and 'h' indicates an includes file.
68_FILE_TYPES = [".v", ".sv", ".vh", ".svh"]
71@click.command(
72 name="format",
73 cls=cmd_util.ApioCommand,
74 short_help="Format verilog source files.",
75 help=APIO_FORMAT_HELP,
76)
77@click.argument("files", nargs=-1, required=False)
78@options.env_option_gen()
79@options.project_dir_option
80@options.verbose_option
81def cli(
82 *,
83 # Arguments
84 files: Tuple[str],
85 env: Optional[str],
86 project_dir: Optional[Path],
87 verbose: bool,
88):
89 """Implements the format command which formats given or all source
90 files to format.
91 """
93 # -- Create an apio context with a project object.
94 apio_ctx = ApioContext(
95 project_policy=ProjectPolicy.PROJECT_REQUIRED,
96 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
97 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
98 project_dir_arg=project_dir,
99 env_arg=env,
100 )
102 # -- Get the optional formatter options from apio.ini
103 cmd_options = apio_ctx.project.get_list_option(
104 "format-verible-options", default=[]
105 )
107 # -- Add verbose option if needed.
108 if verbose and "--verbose" not in cmd_options: 108 ↛ 109line 108 didn't jump to line 109 because the condition on line 108 was never true
109 cmd_options.append("--verbose")
111 # -- Prepare the packages for use.
112 apio_ctx.set_env_for_packages(quiet=not verbose)
114 # -- Convert the tuple with file names into a list.
115 _files: List[str] = list(files)
117 # -- Change to the project's folder.
118 os.chdir(apio_ctx.project_dir)
120 # -- If user didn't specify files to format, all all source files to
121 # -- the list.
122 if not _files:
123 for ext in _FILE_TYPES:
124 _files.extend(glob("**/*" + ext, recursive=True))
126 # -- Filter out files that are under the _build directory.
127 _files = [
128 f for f in _files if PROJECT_BUILD_PATH not in Path(f).parents
129 ]
131 # -- Error if no file to format.
132 if not _files: 132 ↛ 133line 132 didn't jump to line 133 because the condition on line 132 was never true
133 cerror(f"No files of types {_FILE_TYPES}")
134 sys.exit(1)
136 # -- Sort files, case insensitive.
137 _files = sort_files(_files)
139 # -- Iterate the files and format one at a time. We could format
140 # -- all of them at once but this way we can make the output more
141 # -- user friendly.
142 failures = 0
143 for f in _files:
144 # -- Convert to a Path object.
145 path = Path(f)
147 # -- Check the file extension.
148 _, ext = os.path.splitext(path)
149 if ext not in _FILE_TYPES: 149 ↛ 150line 149 didn't jump to line 150 because the condition on line 149 was never true
150 cerror(f"'{f}' has an unexpected extension.")
151 cout(f"Should be one of {_FILE_TYPES}", style=INFO)
152 sys.exit(1)
154 # -- Check that the file exists and is a file.
155 if not path.is_file(): 155 ↛ 156line 155 didn't jump to line 156 because the condition on line 155 was never true
156 cerror(f"'{f}' is not a file.")
157 sys.exit(1)
159 # -- Print file name.
160 styled_f = cstyle(f, style=EMPH3)
161 cout(f"Formatting {styled_f}")
163 # -- Construct the formatter command line.
164 command = (
165 "verible-verilog-format --nofailsafe_success --inplace "
166 f' {" ".join(cmd_options)} "{f}"'
167 )
168 if verbose: 168 ↛ 169line 168 didn't jump to line 169 because the condition on line 168 was never true
169 cout(command)
171 # -- Execute the formatter command line.
172 exit_code = os.system(command)
173 if exit_code != 0: 173 ↛ 174line 173 didn't jump to line 174 because the condition on line 173 was never true
174 cerror(f"Formatting of '{f}' failed")
175 failures += 1
177 # -- Report failures, if eny.
178 if failures: 178 ↛ 179line 178 didn't jump to line 179 because the condition on line 178 was never true
179 cout()
180 cout(
181 f"Encountered {util.plurality(failures, 'failure')}.",
182 style=ERROR,
183 )
184 sys.exit(1)
186 # -- All done ok.
187 cout(f"Processed {util.plurality(_files, 'file')}.", style=SUCCESS)
188 sys.exit(0)