2016-03-18 17:54:07 +00:00
|
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
|
|
|
from __future__ import absolute_import, print_function, unicode_literals
|
|
|
|
|
2016-06-02 19:38:36 +00:00
|
|
|
import argparse
|
2016-03-18 17:54:07 +00:00
|
|
|
import os
|
|
|
|
|
|
|
|
from mozbuild.base import (
|
|
|
|
MachCommandBase,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
from mach.decorators import (
|
|
|
|
CommandArgument,
|
|
|
|
CommandProvider,
|
|
|
|
Command,
|
|
|
|
)
|
|
|
|
|
2018-10-11 20:56:45 +00:00
|
|
|
|
2016-03-18 17:54:07 +00:00
|
|
|
here = os.path.abspath(os.path.dirname(__file__))
|
2018-10-18 15:27:59 +00:00
|
|
|
THIRD_PARTY_PATHS = os.path.join('tools', 'rewriting', 'ThirdPartyPaths.txt')
|
2018-10-12 15:57:44 +00:00
|
|
|
GLOBAL_EXCLUDES = [
|
|
|
|
'tools/lint/test/files',
|
|
|
|
]
|
2016-03-18 17:54:07 +00:00
|
|
|
|
|
|
|
|
2016-06-24 18:06:22 +00:00
|
|
|
def setup_argument_parser():
|
|
|
|
from mozlint import cli
|
|
|
|
return cli.MozlintParser()
|
|
|
|
|
|
|
|
|
2018-10-12 15:57:44 +00:00
|
|
|
def get_global_excludes(topsrcdir):
|
2018-10-18 15:27:59 +00:00
|
|
|
# exclude misc paths
|
2018-10-12 15:57:44 +00:00
|
|
|
excludes = GLOBAL_EXCLUDES[:]
|
2018-10-18 15:27:59 +00:00
|
|
|
|
|
|
|
# exclude top level paths that look like objdirs
|
2018-10-12 15:57:44 +00:00
|
|
|
excludes.extend([name for name in os.listdir(topsrcdir)
|
|
|
|
if name.startswith('obj') and os.path.isdir(name)])
|
2018-10-18 15:27:59 +00:00
|
|
|
|
|
|
|
# exclude third party paths
|
|
|
|
with open(os.path.join(topsrcdir, THIRD_PARTY_PATHS), 'r') as fh:
|
|
|
|
excludes.extend([f.strip() for f in fh.readlines()])
|
|
|
|
|
2018-10-12 15:57:44 +00:00
|
|
|
return excludes
|
|
|
|
|
|
|
|
|
2016-03-18 17:54:07 +00:00
|
|
|
@CommandProvider
|
|
|
|
class MachCommands(MachCommandBase):
|
|
|
|
|
|
|
|
@Command(
|
|
|
|
'lint', category='devenv',
|
2016-06-24 18:06:22 +00:00
|
|
|
description='Run linters.',
|
|
|
|
parser=setup_argument_parser)
|
2016-06-24 18:09:58 +00:00
|
|
|
def lint(self, *runargs, **lintargs):
|
2016-03-18 17:54:07 +00:00
|
|
|
"""Run linters."""
|
Bug 1494069 - [mozlint] Collapse exclude paths into their smallest possible set, r=egao
Often we specify globs in our exclude patterns, e.g:
exclude:
- **/node_modules
- obj*
However, these globs get expanded out to *every* file that matches them. This
can sometimes be thousands or even tens of thousands of files.
We then pass these paths on to the underlying linters and tell them to
exclude them all. This causes a lot of overhead and slows down performance.
This commit implements a "collapse" function. Given a set of paths, it'll
collapse them into the smallest set of parent directories that contain the
original set, and that don't contain any extra files.
For example, given a directory structure like this:
a
-- foo.txt
-- b
-- bar.txt
-- baz.txt
-- c
-- ham.txt
-- d
-- spam.txt
Then the following will happen:
>>> collapse(['a/foo.txt', 'a/b/bar.txt', 'a/c/ham.txt', 'a/c/d/spam.txt'])
['a/foo.txt', 'b/bar.txt', 'c']
Since all files under directory 'c' are specified by the original set (both
'c/ham.txt' and 'c/d/spam.txt'), we can collapse it down to just 'c'. However
not all files under 'b' are specified (we're missing 'a/b/baz.txt'), so we
can't collapse 'b' (and by extension also can't collapse 'a').
If we had included 'a/b/baz.txt':
>>> collapse(['a/foo.txt', 'a/b/bar.txt', 'a/b/baz.txt', 'a/c/ham.txt', 'a/c/d/spam.txt'])
['a']
In both cases, the smallest set of paths that contains the original set (and
only the original set) is computed.
The collapse function has a little bit of overhead but it's not too bad.
For example collapsing all files matched by '**/node_modules' takes ~0.015s.
Collapsing two full objdirs, takes ~0.6s. But a follow up commit is planned to
make sure we stop using 'obj*' to reduce that overhead.
Depends on D7738
Differential Revision: https://phabricator.services.mozilla.com/D7739
--HG--
extra : moz-landing-system : lando
2018-10-12 15:57:42 +00:00
|
|
|
self._activate_virtualenv()
|
2018-10-18 15:27:59 +00:00
|
|
|
from mozlint import cli, parser
|
2018-10-12 15:57:44 +00:00
|
|
|
|
2017-07-22 07:28:34 +00:00
|
|
|
lintargs.setdefault('root', self.topsrcdir)
|
2018-10-12 15:57:44 +00:00
|
|
|
lintargs['exclude'] = get_global_excludes(lintargs['root'])
|
2016-06-24 18:06:22 +00:00
|
|
|
cli.SEARCH_PATHS.append(here)
|
2018-10-18 15:27:59 +00:00
|
|
|
parser.GLOBAL_SUPPORT_FILES.append(os.path.join(self.topsrcdir, THIRD_PARTY_PATHS))
|
2016-06-24 18:09:58 +00:00
|
|
|
return cli.run(*runargs, **lintargs)
|
2016-06-02 19:38:36 +00:00
|
|
|
|
|
|
|
@Command('eslint', category='devenv',
|
|
|
|
description='Run eslint or help configure eslint for optimal development.')
|
2016-08-30 18:28:56 +00:00
|
|
|
@CommandArgument('paths', default=None, nargs='*',
|
|
|
|
help="Paths to file or directories to lint, like "
|
2017-04-21 16:31:15 +00:00
|
|
|
"'browser/' Defaults to the "
|
2016-08-30 18:28:56 +00:00
|
|
|
"current directory if not given.")
|
2016-06-02 19:38:36 +00:00
|
|
|
@CommandArgument('-s', '--setup', default=False, action='store_true',
|
2016-07-22 23:18:58 +00:00
|
|
|
help='Configure eslint for optimal development.')
|
2016-06-02 19:38:36 +00:00
|
|
|
@CommandArgument('-b', '--binary', default=None,
|
|
|
|
help='Path to eslint binary.')
|
2016-08-03 23:18:55 +00:00
|
|
|
@CommandArgument('--fix', default=False, action='store_true',
|
|
|
|
help='Request that eslint automatically fix errors, where possible.')
|
2016-08-30 18:28:56 +00:00
|
|
|
@CommandArgument('extra_args', nargs=argparse.REMAINDER,
|
|
|
|
help='Extra args that will be forwarded to eslint.')
|
2016-10-14 12:20:23 +00:00
|
|
|
def eslint(self, paths, extra_args=[], **kwargs):
|
2016-08-30 18:28:56 +00:00
|
|
|
self._mach_context.commands.dispatch('lint', self._mach_context,
|
2016-10-14 12:20:23 +00:00
|
|
|
linters=['eslint'], paths=paths,
|
|
|
|
argv=extra_args, **kwargs)
|