-
Notifications
You must be signed in to change notification settings - Fork 388
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
New subcommand infrastructure + splitting out log command #610
Conversation
Due to the log command already being refurbished and thus we nevertheless would need to rebase this, I am introducing the fix for #580 here. Once again, thank you @rizsotto for the prompt reply at rizsotto/scan-build#79. |
bin/CodeChecker.py
Outdated
main() | ||
# Load the available CodeChecker subcommands. | ||
# This list is generated dynamically by scripts/build_package.py | ||
version_cfg = os.path.join(os.environ['CC_PACKAGE_ROOT'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not depend on environment variables. I try to remove them. I think we should hardcode the path here or create a better solution for it. (move it into a python module and import it?)
libcodechecker/log/host_check.py
Outdated
try: | ||
res = subprocess.call(intercept_cmd, | ||
env=env, | ||
stdout=subprocess.PIPE, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a note not to use subprocess.PIPE
in the call
because it can deadlock. popen
and communicate
is the recommended solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't even need to properly communicate with intercept-build
here, we only need to see if the command returns properly.
scripts/build_package.py
Outdated
@@ -639,9 +639,35 @@ def build_package(repository_root, build_package_config, env=None): | |||
time_now = time.strftime("%Y-%m-%dT%H:%M") | |||
version_json_data['package_build_date'] = time_now | |||
|
|||
version_json_data['available_commands'] = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we put the subcommands into the version config file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The version file is the one which contains the most build-time created information, such at git describe and the like.
I felt it is appropriate to put the information here as the list of available commands is a property of a particular build taken, ie. users could arbitrarily add or remove commands before building their package.
libcodechecker/analyze/log_parser.py
Outdated
elif 'arguments' in entry: | ||
# Newest versions of intercept-build create an argument vector | ||
# instead of a command string. | ||
command = ' '.join(entry['arguments']) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the argument list is ["one", "two three"]
then you concatenate it as you do it here, you won't be able to get the original list back with split. You need to annotate the list items better. (Or if you won't do the split by yourself, the called shell will do it. So you need shell escaping. How portable is that?) The main driver to change this in intercept-build
was to get rid of these concatenation and splitting errors. The current compilation database parser in Clang understands the arguments
filed... So the question, why don't you pass the command around as a list instead of a string?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changes in this patch are specifically around creating a new architecture for the commands. There are already at least one issue about argument list escaping, #505 comes to mind. The moment we start to refurbish the check
command, we'll look into these issues.
Right now, ld-logger
puts custom arguments from
CodeChecker check -n run -b "g++ -DVAR='va lu e' main.cpp -o a.out"
to the build.json as: \"-DVAR=va lu e\"
but these values – and I've checked it just now – do not reach the analyzer command.
In case of intercept-build
, however, -DVAR=va
reaches the analyzer. I will now make sure the same escape sequence is applied to the command here, and we will discuss proper argument forwarding later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure that i get the question... the point i was trying to rise was that when you read the compilation database, it's better to keep arguments
as list (don't concatenate it) and if it was a command
make it as a list (with shlex.split
or something clever)... So either way, you get a list of arguments... So, you can call subprocess.call
or subprocess.check_call
without shell=True
. (Which will make your code more portable and get rid of the problem i've mentioned before.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the record --- I've already removed the question because I found the solution meanwhile, the question was:
How would you pass an argument list to clang, as right now the
build.json
never directly reaches the analyzer?
The build database is parsed and filtered, and from that do we create the analyzer invocation.
I've investigated the issue and came up with the solution that works both for intercept-build and ld-logger, while also esentially fixing the aforementioned issue #505. Please see #631.
7497433
to
b90acea
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have one question, otherwise looks good.
CC = os.path.join(THIS_PATH, "CodeChecker") | ||
|
||
# Load CodeChecker from the current folder (the wrapper script (without .py)) | ||
CodeChecker = imp.load_source('CodeChecker', CC) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reason for using imp here? Can't we load it with the regular import syntax?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. The folder where the environment bootstrapper resides is not in the PYTHONPATH
. Also it's not a real Python package either, and importing from relative paths outside a package is an error, and inside a package is still discouraged.
See, for example, this question on SO. We couldn't use from ..bin
because we can't make assumptions on where the CC scripts that go into the users' PATH
are installed.
Could we squash some of the commits?
|
3b50fbc
to
23b495d
Compare
Sure, done. 😉 |
23b495d
to
8930d8c
Compare
8930d8c
to
4137434
Compare
This PR introduces a new command infrastructure to help in further compartmentalising CodeChecker which begun in #584. Closes #593.
Overview
New entrypoints
New CodeChecker entrypoints are introduced, following the "usual scheme" set out by the giants ahead of us. In line with, e.g. how Git works (
git log
andgit-log
are both commands), we will now have each "part"/"subcommand"/"feature" of CodeChecker as a direct entry point, such ascodechecker-log
. This, in the future, will help us create more dynamic helps, such as man pages for a Debian package, or a TAB autocompletion support (#576).In reality,
codechecker-log
andCodeChecker log
has the same behaviour,codechecker-log
is just a directly callable wrapper script that is also put in thePATH
.Putting the argument list and the executed function together
libcodechecker/arg_handler.py
is a voloptous patchwork, which imports everything from all parts of CodeChecker. While the executed code are in this file, their help and argument definitions are inbin/CodeChecker.py
...From now on, subcommands of CodeChecker will be located in their own file,
libcodechecker/log.py
, which has an easy interface.get_argparser_ctor_args
returns a dict which specifies how the subcommand argument parser should be constructedadd_arguments_to_parser(parser)
is the function which puts the arguments into the constructed argument parser.main(args)
executes the method of the subcommand. (Hereargs
is a parsedargparse.Namespace
.)Dynamic package building and loading
The
CodeChecker.py
script will now dynamically load the available subcommands' help and argument list. This dynamic list is created at package build via the install script. However, if the user supplies a valid subcommand (either callingCodeChecker log
orcodechecker-log
, the two are equal), the helpers, and executed actions (and thus, every library module that executed function needs) for all the other commands will not be loaded.While CodeChecker is already really fast on most machines, I hope this will help to increase speed even more, and this will definitely help to further compartmentalise CodeChecker.
All the above, as of this PR, only applies to
CodeChecker log
, the remaining commands are untouched, for now.Helpers
Just like how the test framework is given a "bootstrap script" to create new test cases, I'm introducing
scripts/create_new_subcommand.py
which, when called with a singlesubcommand-name
argument, will create, in the development directory, the neccessary skeleton for a new command,CodeChecker subcommand-name
(orcodechecker-subcommand-name
).Also, as a minor change, I've updated the
CodeChecker log
's help message a bit.