diff --git a/python/requirements.txt b/python/requirements.txt index c57a74825da..97487c76aff 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -14,4 +14,7 @@ pyflakes == 0.8.1 # For test-webidl ply == 3.8 +# For Cross-platform colored terminal text +colorama == 0.3.7 + -e python/tidy diff --git a/python/tidy/HISTORY.rst b/python/tidy/HISTORY.rst index 1e02962afa0..bc36c7e908f 100644 --- a/python/tidy/HISTORY.rst +++ b/python/tidy/HISTORY.rst @@ -1,6 +1,17 @@ Release History --------------- +0.1.0 (2016-08-09) +++++++++++++++++++ + +- Improve license checking to disregard comments and line breaks +- License checking verifies that COPYRIGHT is specified when apache2 is used + +0.0.3 (2016-04-19) +++++++++++++++++++ + +- Add alternate wording of apache2 license + 0.0.2 (2016-04-17) ++++++++++++++++++ - Cleanup Tidy to work on external deps diff --git a/python/tidy/servo_tidy/licenseck.py b/python/tidy/servo_tidy/licenseck.py index 3a17f1fdf04..a4953e6eabb 100644 --- a/python/tidy/servo_tidy/licenseck.py +++ b/python/tidy/servo_tidy/licenseck.py @@ -7,97 +7,24 @@ # option. This file may not be copied, modified, or distributed # except according to those terms. +MPL = """\ +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/.\ +""" -# These licenses are valid for use in Servo -licenses = [ +APACHE = """\ +Licensed under the Apache License, Version 2.0 or the MIT license \ +, at your \ +option. This file may not be copied, modified, or distributed \ +except according to those terms.\ +""" -"""\ -/* 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/. */ -""", - -"""\ -# 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/. -""", - -"""\ -#!/usr/bin/env bash - -# 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/. -""", - -"""\ -#!/usr/bin/env python - -# 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/. -""", - -"""\ -#!/usr/bin/env python3 - -# 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/. -""", - -"""\ -// 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/. -""", - -"""\ -// Copyright 2013 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -""", - -"""\ -# Copyright 2013 The Servo Project Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. -""", - -"""\ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -""", - -"""\ -// Copyright 2012-2014 The Rust Project Developers. -// See http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -""", -] # noqa: Indicate to flake8 that we do not want to check indentation here +COPYRIGHT = [ + "See the COPYRIGHT file at the top-level directory of this distribution", + "See http://rust-lang.org/COPYRIGHT", +] # The valid licenses, in the form we'd expect to see them in a Cargo.toml file. licenses_toml = [ diff --git a/python/tidy/servo_tidy/tidy.py b/python/tidy/servo_tidy/tidy.py index b21d65259fb..d6d6344db54 100644 --- a/python/tidy/servo_tidy/tidy.py +++ b/python/tidy/servo_tidy/tidy.py @@ -17,12 +17,10 @@ import site import StringIO import subprocess import sys -from licenseck import licenses, licenses_toml, licenses_dep_toml +from licenseck import MPL, APACHE, COPYRIGHT, licenses_toml, licenses_dep_toml +import colorama -# License and header checks -EMACS_HEADER = "/* -*- Mode:" -VIM_HEADER = "/* vim:" -MAX_LICENSE_LINESPAN = max(len(license.splitlines()) for license in licenses) +COMMENTS = ["// ", "# ", " *", "/* "] # File patterns to include in the non-WPT tidy check. FILE_PATTERNS_TO_CHECK = ["*.rs", "*.rc", "*.cpp", "*.c", @@ -60,7 +58,6 @@ IGNORED_FILES = [ IGNORED_DIRS = [ # Upstream os.path.join(".", "support", "android", "apk"), - os.path.join(".", "support", "rust-task_info"), os.path.join(".", "tests", "wpt", "css-tests"), os.path.join(".", "tests", "wpt", "harness"), os.path.join(".", "tests", "wpt", "update"), @@ -152,13 +149,45 @@ def filter_files(start_dir, only_changed_files, progress): yield file_name +def uncomment(line): + for c in COMMENTS: + if line.startswith(c): + if line.endswith("*/"): + return line[len(c):(len(line) - 3)].strip() + return line[len(c):].strip() + + +def licensed_mpl(header): + return MPL in header + + +def licensed_apache(header): + if APACHE in header: + return any(c in header for c in COPYRIGHT) + + def check_license(file_name, lines): if any(file_name.endswith(ext) for ext in (".toml", ".lock", ".json", ".html")): raise StopIteration - while lines and (lines[0].startswith(EMACS_HEADER) or lines[0].startswith(VIM_HEADER)): - lines = lines[1:] - contents = "".join(lines[:MAX_LICENSE_LINESPAN]) - valid_license = any(contents.startswith(license) for license in licenses) + + if lines[0].startswith("#!") and lines[1].strip(): + yield (1, "missing blank line after shebang") + + blank_lines = 0 + max_blank_lines = 2 if lines[0].startswith("#!") else 1 + license_block = [] + + for l in lines: + l = l.rstrip('\n') + if not l.strip(): + blank_lines += 1 + if blank_lines >= max_blank_lines: + break + line = uncomment(l) + if line is not None: + license_block.append(line) + contents = " ".join(license_block) + valid_license = licensed_mpl(contents) or licensed_apache(contents) acknowledged_bad_license = "xfail-license" in contents if not (valid_license or acknowledged_bad_license): yield (1, "incorrect license") @@ -310,8 +339,8 @@ def check_toml(file_name, lines): for idx, line in enumerate(lines): if line.find("*") != -1: yield (idx + 1, "found asterisk instead of minimum version number") - for license in licenses_toml: - ok_licensed |= (license in line) + for license_line in licenses_toml: + ok_licensed |= (license_line in line) if not ok_licensed: yield (0, ".toml file should contain a valid license.") @@ -762,8 +791,10 @@ def scan(only_changed_files=False, progress=True): errors = itertools.chain(errors, dep_license_errors, wpt_lint_errors) error = None for error in errors: + colorama.init() print "\r\033[94m{}\033[0m:\033[93m{}\033[0m: \033[91m{}\033[0m".format(*error) print if error is None: + colorama.init() print "\033[92mtidy reported no errors.\033[0m" return int(error is not None) diff --git a/python/tidy/servo_tidy_tests/apache2_license.rs b/python/tidy/servo_tidy_tests/apache2_license.rs new file mode 100644 index 00000000000..c9fed89cf73 --- /dev/null +++ b/python/tidy/servo_tidy_tests/apache2_license.rs @@ -0,0 +1,5 @@ +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. diff --git a/python/tidy/servo_tidy_tests/shebang_license.py b/python/tidy/servo_tidy_tests/shebang_license.py new file mode 100644 index 00000000000..0499f3a93f9 --- /dev/null +++ b/python/tidy/servo_tidy_tests/shebang_license.py @@ -0,0 +1,4 @@ +#!/usr/bin/env python +# 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/. diff --git a/python/tidy/servo_tidy_tests/test_tidy.py b/python/tidy/servo_tidy_tests/test_tidy.py index 92b304a5c1a..bb1cb8ce61c 100644 --- a/python/tidy/servo_tidy_tests/test_tidy.py +++ b/python/tidy/servo_tidy_tests/test_tidy.py @@ -48,6 +48,11 @@ class CheckTidiness(unittest.TestCase): self.assertEqual('incorrect license', errors.next()[2]) self.assertNoMoreErrors(errors) + def test_shebang_license(self): + errors = tidy.collect_errors_for_files(iterFile('shebang_license.py'), [], [tidy.check_license], print_text=False) + self.assertEqual('missing blank line after shebang', errors.next()[2]) + self.assertNoMoreErrors(errors) + def test_shell(self): errors = tidy.collect_errors_for_files(iterFile('shell_tidy.sh'), [], [tidy.check_shell], print_text=False) self.assertEqual('script does not have shebang "#!/usr/bin/env bash"', errors.next()[2]) @@ -58,6 +63,10 @@ class CheckTidiness(unittest.TestCase): self.assertEqual('script should use `[[` instead of `[` for conditional testing', errors.next()[2]) self.assertNoMoreErrors(errors) + def test_apache2_incomplete(self): + errors = tidy.collect_errors_for_files(iterFile('apache2_license.rs'), [], [tidy.check_license]) + self.assertEqual('incorrect license', errors.next()[2]) + def test_rust(self): errors = tidy.collect_errors_for_files(iterFile('rust_tidy.rs'), [], [tidy.check_rust], print_text=False) self.assertEqual('use statement spans multiple lines', errors.next()[2]) diff --git a/python/tidy/setup.py b/python/tidy/setup.py index a1af7d88465..80d2140f3ec 100644 --- a/python/tidy/setup.py +++ b/python/tidy/setup.py @@ -11,11 +11,12 @@ import os from setuptools import setup, find_packages -VERSION = '0.0.3' +VERSION = '0.1.0' install_requires = [ "flake8==2.4.1", "toml==0.9.1", + "colorama==0.3.7", ] here = os.path.dirname(os.path.abspath(__file__)) @@ -51,7 +52,7 @@ if __name__ == '__main__': zip_safe=False, entry_points={ 'console_scripts': [ - 'servo-tidy=servo_tidy.tidy:scan' + 'servo-tidy=servo_tidy.tidy:scan', ], }, ) diff --git a/support/rust-task_info/Cargo.toml b/support/rust-task_info/Cargo.toml index 499bd39f5f3..b65a569a21a 100644 --- a/support/rust-task_info/Cargo.toml +++ b/support/rust-task_info/Cargo.toml @@ -3,6 +3,7 @@ name = "task_info" version = "0.0.1" authors = ["The Servo Project Developers"] +license = "MPL-2.0" build = "build.rs"