mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-14 15:56:38 +03:00
83cb273c22
The "No changes detected" warning applies per output file, but the file wasn't included in the log, which was confusing to me. In addition to that, many of the builds depends on locales.js, so the log message got duplicated several times because of that when you build `./build/all.py` This PR adds module level caching for build/compiler to avoid repetition and tries to make the log more informative. It uses the output file basename, which seems like a safe assumption currently since the dist output structure is flat. But if this is a deal breaker maybe we can print the full path or the relative path from the shaka repo root instead. This is the log for the most extreme case (`./build/all.py` directly after another build) before the PR: ``` [WARNING] No changes detected, skipping. Use --force to override. [INFO] Generating Closure dependencies... [INFO] Linting JavaScript... [WARNING] No changes detected, skipping. Use --force to override. [INFO] Linting CSS... [WARNING] No changes detected, skipping. Use --force to override. [INFO] Linting HTML... [WARNING] No changes detected, skipping. Use --force to override. [INFO] Checking that the build files are complete... [INFO] Checking for spelling mistakes in js files... CSpell: Files checked: 578, Issues found: 0 in 0 files. [INFO] Checking for spelling mistakes in md files... CSpell: Files checked: 49, Issues found: 0 in 0 files. [INFO] Checking for spelling mistakes in py files... CSpell: Files checked: 16, Issues found: 0 in 0 files. [INFO] Checking the tests for type errors... [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [INFO] Building the docs... [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [INFO] Compiling the library (experimental, debug)... [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [INFO] Compiling the library (ui, debug)... [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [INFO] Compiling the library (compiled, debug)... [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [INFO] Compiling the library (dash, debug)... [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [INFO] Compiling the library (hls, debug)... [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [INFO] Compiling the demo app (debug)... [WARNING] No changes detected, skipping. Use --force to override. [INFO] Compiling the receiver app (debug)... [WARNING] No changes detected, skipping. Use --force to override. [INFO] Compiling the library (experimental, release)... [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [INFO] Compiling the library (ui, release)... [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [INFO] Compiling the library (compiled, release)... [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [INFO] Compiling the library (dash, release)... [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [INFO] Compiling the library (hls, release)... [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [WARNING] No changes detected, skipping. Use --force to override. [INFO] Compiling the demo app (release)... [WARNING] No changes detected, skipping. Use --force to override. [INFO] Compiling the receiver app (release)... [WARNING] No changes detected, skipping. Use --force to override. ``` With this PR it will print this instead: ``` [WARNING] Detected output files that do not need to be rebuilt. Use --force to override. [INFO] Skipping locales.js (already built) [INFO] Generating Closure dependencies... [INFO] Linting JavaScript... [INFO] Skipping .lintstamp (already built) [INFO] Linting CSS... [INFO] Skipping .csslintstamp (already built) [INFO] Linting HTML... [INFO] Skipping .htmllintstamp (already built) [INFO] Checking that the build files are complete... [INFO] Checking for spelling mistakes in js files... CSpell: Files checked: 578, Issues found: 0 in 0 files. [INFO] Checking for spelling mistakes in md files... CSpell: Files checked: 49, Issues found: 0 in 0 files. [INFO] Checking for spelling mistakes in py files... CSpell: Files checked: 16, Issues found: 0 in 0 files. [INFO] Checking the tests for type errors... [INFO] Skipping .testcheckstamp (already built) [INFO] Building the docs... [INFO] Skipping index.html (already built) [INFO] Skipping controls.css (already built) [INFO] Skipping demo.css (already built) [INFO] Compiling the library (experimental, debug)... [INFO] Skipping shaka-player.experimental.debug.js (already built) [INFO] Skipping shaka-player.experimental.debug.externs.js (already built) [INFO] Skipping shaka-player.experimental.debug.d.ts (already built) [INFO] Compiling the library (ui, debug)... [INFO] Skipping shaka-player.ui.debug.js (already built) [INFO] Skipping shaka-player.ui.debug.externs.js (already built) [INFO] Skipping shaka-player.ui.debug.d.ts (already built) [INFO] Compiling the library (compiled, debug)... [INFO] Skipping shaka-player.compiled.debug.js (already built) [INFO] Skipping shaka-player.compiled.debug.externs.js (already built) [INFO] Skipping shaka-player.compiled.debug.d.ts (already built) [INFO] Compiling the library (dash, debug)... [INFO] Skipping shaka-player.dash.debug.js (already built) [INFO] Skipping shaka-player.dash.debug.externs.js (already built) [INFO] Skipping shaka-player.dash.debug.d.ts (already built) [INFO] Compiling the library (hls, debug)... [INFO] Skipping shaka-player.hls.debug.js (already built) [INFO] Skipping shaka-player.hls.debug.externs.js (already built) [INFO] Skipping shaka-player.hls.debug.d.ts (already built) [INFO] Compiling the demo app (debug)... [INFO] Skipping demo.compiled.debug.js (already built) [INFO] Compiling the receiver app (debug)... [INFO] Skipping receiver.compiled.debug.js (already built) [INFO] Compiling the library (experimental, release)... [INFO] Skipping shaka-player.experimental.js (already built) [INFO] Skipping shaka-player.experimental.externs.js (already built) [INFO] Skipping shaka-player.experimental.d.ts (already built) [INFO] Compiling the library (ui, release)... [INFO] Skipping shaka-player.ui.js (already built) [INFO] Skipping shaka-player.ui.externs.js (already built) [INFO] Skipping shaka-player.ui.d.ts (already built) [INFO] Compiling the library (compiled, release)... [INFO] Skipping shaka-player.compiled.js (already built) [INFO] Skipping shaka-player.compiled.externs.js (already built) [INFO] Skipping shaka-player.compiled.d.ts (already built) [INFO] Compiling the library (dash, release)... [INFO] Skipping shaka-player.dash.js (already built) [INFO] Skipping shaka-player.dash.externs.js (already built) [INFO] Skipping shaka-player.dash.d.ts (already built) [INFO] Compiling the library (hls, release)... [INFO] Skipping shaka-player.hls.js (already built) [INFO] Skipping shaka-player.hls.externs.js (already built) [INFO] Skipping shaka-player.hls.d.ts (already built) [INFO] Compiling the demo app (release)... [INFO] Skipping demo.compiled.js (already built) [INFO] Compiling the receiver app (release)... [INFO] Skipping receiver.compiled.js (already built) ```
578 lines
19 KiB
Python
578 lines
19 KiB
Python
# Copyright 2016 Google LLC
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""Classes representing the various compiler and linter tools that are used to
|
|
build Shaka Player."""
|
|
|
|
import json
|
|
import logging
|
|
import os
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
|
|
import generateLocalizations
|
|
import shakaBuildHelpers
|
|
|
|
|
|
_force_hint_shown = False
|
|
_skip_messages_shown = set()
|
|
|
|
|
|
def _canonicalize_source_files(source_files):
|
|
"""Canonicalize a set or list of source files.
|
|
|
|
This makes all path names cygwin-safe and sorted."""
|
|
|
|
files = [shakaBuildHelpers.cygwin_safe_path(f) for f in source_files]
|
|
files.sort()
|
|
return files
|
|
|
|
def _get_source_path(path):
|
|
"""Take path components of a source file as arguments and return an absolute,
|
|
cygwin-safe path."""
|
|
return shakaBuildHelpers.cygwin_safe_path(os.path.join(
|
|
shakaBuildHelpers.get_source_base(), path))
|
|
|
|
def _must_build(output, source_files):
|
|
"""Returns True if any of the |source_files| have changed since |output| was
|
|
built, or if |output| does not exist yet."""
|
|
global _force_hint_shown, _skip_messages_shown
|
|
|
|
if not os.path.isfile(output):
|
|
# Nothing built, so we should build the output.
|
|
return True
|
|
|
|
# Detect changes to the set of files that we intend to build.
|
|
build_time = os.path.getmtime(output)
|
|
# See if any files were modified since the output was created.
|
|
if any(os.path.getmtime(f) > build_time for f in source_files):
|
|
# Some input files have changed, so we should build again.
|
|
return True
|
|
|
|
# Look at all the Python modules that are loaded. If any of them have
|
|
# changed, it may affect the build.
|
|
this_path = os.path.dirname(os.path.abspath(__file__))
|
|
for module in sys.modules.values():
|
|
path = getattr(module, '__file__', None)
|
|
if path and os.path.exists(path) and os.path.getmtime(path) > build_time:
|
|
return True
|
|
|
|
# Inform about the --force flag once (if something is skipped)
|
|
if not _force_hint_shown:
|
|
logging.warning('Detected output files that do not need to be rebuilt. Use --force to override.')
|
|
_force_hint_shown = True
|
|
|
|
# Log skip message once per output file
|
|
output_basename = os.path.basename(output)
|
|
if output_basename not in _skip_messages_shown:
|
|
logging.info('Skipping %s (already built)', output_basename)
|
|
_skip_messages_shown.add(output_basename)
|
|
|
|
|
|
return False
|
|
|
|
def _update_timestamp(path):
|
|
# This creates the file if it does not exist, and updates the timestamp if it
|
|
# does.
|
|
open(path, 'wb').close()
|
|
|
|
def _get_java_major_version():
|
|
obj = shakaBuildHelpers.execute_subprocess(
|
|
['java', '-version'], stderr=subprocess.PIPE)
|
|
# This will block until the process terminates, storing stderr in a string
|
|
stderr = obj.communicate()[1]
|
|
|
|
first_line = stderr.decode('utf-8').split('\n')[0]
|
|
# Example: openjdk version "25" 2025-09-16
|
|
# Example: openjdk version "24.0.1" 2025-04-15
|
|
# Example: java version "24.0.2" 2025-07-15
|
|
major_version_match = re.match(r'.*"(\d+).*?".*', first_line)
|
|
major_version_string = major_version_match.group(1)
|
|
|
|
try:
|
|
return int(major_version_string)
|
|
except:
|
|
return 0
|
|
|
|
|
|
class ClosureCompiler(object):
|
|
def __init__(self, source_files, build_name):
|
|
self.source_files = _canonicalize_source_files(source_files)
|
|
|
|
prefix = _get_source_path('dist/' + build_name)
|
|
self.compiled_js_path = prefix + '.js'
|
|
self.source_map_path = prefix + '.map'
|
|
|
|
# These can be overridden for special cases:
|
|
|
|
# If True, output the compiled bundle to a file.
|
|
self.output_compiled_bundle = True
|
|
# If True, generate a source map and attach it to the output bundle.
|
|
self.add_source_map = True
|
|
# If True, wrap the output in a wrapper that prevents window pollution.
|
|
self.add_wrapper = True
|
|
# If not None, use a timestamp file for change detection, and touch that
|
|
# timestamp file after compilation.
|
|
self.timestamp_file = None
|
|
|
|
# Arguments passed to the Java interpreter before launching a jar.
|
|
self.java_opts = []
|
|
|
|
java_major_version = _get_java_major_version()
|
|
if java_major_version >= 23:
|
|
# Silence warnings caused by using the Closure Compiler with newer
|
|
# versions of Java. See https://openjdk.org/jeps/498
|
|
self.java_opts.append('--sun-misc-unsafe-memory-access=allow')
|
|
|
|
jar = _get_source_path(
|
|
'node_modules/google-closure-compiler-java/compiler.jar')
|
|
|
|
# Full compiler command.
|
|
self.compiler_command = ['java'] + self.java_opts + ['-jar', jar]
|
|
|
|
def compile(self, options, force=False):
|
|
"""Builds the files in |self.source_files| using the given Closure
|
|
command-line options.
|
|
|
|
Args:
|
|
options: An array of options to give to Closure.
|
|
force: Generate the output even if the inputs have not changed.
|
|
|
|
Returns:
|
|
True on success; False on failure.
|
|
"""
|
|
if not force:
|
|
if self.timestamp_file:
|
|
if not _must_build(self.timestamp_file, self.source_files):
|
|
return True
|
|
else:
|
|
if not _must_build(self.compiled_js_path, self.source_files):
|
|
return True
|
|
|
|
output_options = []
|
|
if self.output_compiled_bundle:
|
|
output_options += [
|
|
'--js_output_file', self.compiled_js_path,
|
|
]
|
|
|
|
if self.add_source_map:
|
|
source_base = _get_source_path('')
|
|
|
|
output_options += [
|
|
'--create_source_map', self.source_map_path,
|
|
# This uses a simple string replacement to create relative paths.
|
|
# "source|replacement".
|
|
'--source_map_location_mapping', source_base + '|../',
|
|
]
|
|
if shakaBuildHelpers.is_windows() or shakaBuildHelpers.is_cygwin():
|
|
output_options += [
|
|
# On Windows, the source map needs to use '/' for paths, so we
|
|
# need to have this mapping so it creates the correct relative
|
|
# paths. For some reason, we still need the mapping above for
|
|
# other parts of the source map.
|
|
'--source_map_location_mapping',
|
|
source_base.replace('\\', '/') + '|../',
|
|
]
|
|
|
|
if self.add_wrapper:
|
|
output_options += self._prepare_wrapper()
|
|
|
|
# Write a temp file with the file list as quoted strings.
|
|
# This will be deleted only when exiting the context manager.
|
|
with shakaBuildHelpers.NamedTemporaryFile() as fp:
|
|
normal_flags = output_options + options
|
|
quoted_files = '\n'.join(
|
|
[shakaBuildHelpers.quote_argument(x) for x in self.source_files]) + '\n'
|
|
|
|
if os.environ.get('PRINT_ARGUMENTS'):
|
|
logging.info('Compiling these files:\n' + quoted_files)
|
|
|
|
fp.write(quoted_files.encode('utf8'))
|
|
fp.close()
|
|
|
|
# To avoid command line length limits on Windows, the list of files are
|
|
# read from the temp file. We still put normal command line flags in the
|
|
# command line.
|
|
# cspell: disable-next-line
|
|
cmd_line = self.compiler_command + normal_flags + ['--flagfile', fp.name]
|
|
if shakaBuildHelpers.execute_get_code(cmd_line) != 0:
|
|
logging.error('Build failed')
|
|
return False
|
|
|
|
if self.output_compiled_bundle and self.add_source_map:
|
|
# Add a special source-mapping comment so that Chrome and Firefox can map
|
|
# line and character numbers from the compiled library back to the
|
|
# original source locations.
|
|
with shakaBuildHelpers.open_file(self.compiled_js_path, 'a') as f:
|
|
f.write('//# sourceMappingURL=%s' % os.path.basename(
|
|
self.source_map_path))
|
|
|
|
if self.timestamp_file:
|
|
_update_timestamp(self.timestamp_file)
|
|
|
|
return True
|
|
|
|
def _prepare_wrapper(self):
|
|
"""Prepares an output wrapper and returns a list of command line arguments
|
|
for Closure Compiler to use it."""
|
|
|
|
# Load the wrapper and use Closure to strip whitespace and comments.
|
|
# This requires %output% in the template to be protected, so Closure doesn't
|
|
# fail to parse it.
|
|
wrapper_input_path = _get_source_path('build/wrapper.template.js')
|
|
wrapper_output_path = _get_source_path('dist/wrapper.js')
|
|
|
|
with shakaBuildHelpers.open_file(wrapper_input_path, 'r') as f:
|
|
wrapper_code = f.read().replace('%output%', '"%output%"')
|
|
|
|
cmd_line = self.compiler_command + ['-O', 'WHITESPACE_ONLY']
|
|
|
|
proc = shakaBuildHelpers.execute_subprocess(
|
|
cmd_line, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE)
|
|
stripped_wrapper_code, stderr = proc.communicate(wrapper_code.encode('utf8'))
|
|
|
|
if proc.returncode != 0:
|
|
# Print stderr to help diagnose the failure (e.g., Java version errors)
|
|
if stderr:
|
|
stderr_text = stderr.decode('utf-8', errors='replace')
|
|
logging.error('Closure Compiler failed with stderr:\n%s', stderr_text)
|
|
raise RuntimeError('Failed to strip whitespace from wrapper!')
|
|
|
|
with shakaBuildHelpers.open_file(wrapper_output_path, 'w') as f:
|
|
code = stripped_wrapper_code.decode('utf8')
|
|
f.write(code.replace('"%output%"', '%output%'))
|
|
|
|
return ['--output_wrapper_file=%s' % wrapper_output_path]
|
|
|
|
|
|
class ExternGenerator(object):
|
|
def __init__(self, source_files, build_name):
|
|
self.source_files = _canonicalize_source_files(source_files)
|
|
self.output = _get_source_path('dist/' + build_name + '.externs.js')
|
|
|
|
def generate(self, force=False):
|
|
"""Generates externs for the files in |self.source_files|.
|
|
|
|
Args:
|
|
force: Generate the output even if the inputs have not changed.
|
|
|
|
Returns:
|
|
True on success; False on failure.
|
|
"""
|
|
if not force and not _must_build(self.output, self.source_files):
|
|
return True
|
|
|
|
extern_generator = _get_source_path('build/generateExterns.js')
|
|
|
|
cmd_line = ['node', extern_generator, '--output', self.output]
|
|
cmd_line += self.source_files
|
|
|
|
if shakaBuildHelpers.execute_get_code(cmd_line) != 0:
|
|
logging.error('Externs generation failed')
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
class TsDefGenerator(object):
|
|
def __init__(self, source_files, build_name):
|
|
self.source_files = _canonicalize_source_files(source_files)
|
|
self.output = _get_source_path('dist/' + build_name + '.d.ts')
|
|
|
|
def generate(self, force=False):
|
|
"""Generates externs for the files in |self.source_files|.
|
|
|
|
Args:
|
|
force: Generate the output even if the inputs have not changed.
|
|
|
|
Returns:
|
|
True on success; False on failure.
|
|
"""
|
|
if not force and not _must_build(self.output, self.source_files):
|
|
return True
|
|
|
|
def_generator = _get_source_path('build/generateTsDefs.py')
|
|
|
|
cmd_line = [sys.executable or 'python', def_generator, '--output',
|
|
self.output]
|
|
cmd_line += self.source_files
|
|
|
|
if shakaBuildHelpers.execute_get_code(cmd_line) != 0:
|
|
logging.error('TS defs generation failed')
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
class Less(object):
|
|
def __init__(self, main_source_file, all_source_files, output):
|
|
# Less only takes one input file, but that input may import others.
|
|
# We use main_source_file for compilation, but all_source_files to detect
|
|
# if it needs to be rebuilt.
|
|
self.main_source_file = _canonicalize_source_files([main_source_file])[0]
|
|
self.all_source_files = _canonicalize_source_files(all_source_files)
|
|
self.output = output
|
|
|
|
def compile(self, force=False):
|
|
"""Compiles the main less file in |self.main_source_file| into the
|
|
|self.output| css file.
|
|
|
|
Args:
|
|
force: Generate the output even if the inputs have not changed.
|
|
|
|
Returns:
|
|
True on success; False on failure.
|
|
"""
|
|
if not force and not _must_build(self.output, self.all_source_files):
|
|
return True
|
|
|
|
lessc = shakaBuildHelpers.get_node_binary('less', 'lessc')
|
|
less_options = [
|
|
# Enable the "clean-CSS" plugin to minify the output and strip out comments.
|
|
'--clean-css',
|
|
# Output a source map of the original CSS/less files.
|
|
'--source-map=' + self.output + '.map',
|
|
]
|
|
|
|
cmd_line = lessc + less_options + [self.main_source_file, self.output]
|
|
|
|
if shakaBuildHelpers.execute_get_code(cmd_line) != 0:
|
|
logging.error('CSS compilation failed')
|
|
return False
|
|
|
|
# We need to prepend the license header to the compiled CSS.
|
|
with open(_get_source_path('build/license-header'), 'rb') as f:
|
|
license_header = f.read()
|
|
with open(self.output, 'rb') as f:
|
|
contents = f.read()
|
|
with open(self.output, 'wb') as f:
|
|
f.write(license_header)
|
|
f.write(contents)
|
|
|
|
return True
|
|
|
|
|
|
class Linter(object):
|
|
def __init__(self, source_files, config_path):
|
|
self.source_files = _canonicalize_source_files(source_files)
|
|
self.config_path = config_path
|
|
self.output = _get_source_path('dist/.lintstamp')
|
|
|
|
def lint(self, fix=False, force=False):
|
|
"""Run linter checks on the files in |self.source_files|.
|
|
|
|
Args:
|
|
fix: If True, ask the linter to fix what errors it can automatically.
|
|
force: Run linter checks even if the inputs have not changed.
|
|
|
|
Returns:
|
|
True on success; False on failure.
|
|
"""
|
|
deps = self.source_files + [self.config_path]
|
|
if not force and not _must_build(self.output, deps):
|
|
return True
|
|
|
|
eslint = shakaBuildHelpers.get_node_binary('eslint')
|
|
cmd_line = eslint + ['--config', self.config_path] + self.source_files
|
|
|
|
if fix:
|
|
cmd_line += ['--fix']
|
|
|
|
if shakaBuildHelpers.execute_get_code(cmd_line) != 0:
|
|
return False
|
|
|
|
# TODO: Add back Closure Compiler Linter
|
|
|
|
# Update the timestamp of the file that tracks when we last updated.
|
|
_update_timestamp(self.output)
|
|
return True
|
|
|
|
|
|
class CssLinter(object):
|
|
def __init__(self, source_files, config_path):
|
|
self.source_files = _canonicalize_source_files(source_files)
|
|
self.config_path = config_path
|
|
self.output = _get_source_path('dist/.csslintstamp')
|
|
|
|
def lint(self, fix=False, force=False):
|
|
"""Run CSS linter checks on the files in |self.source_files|.
|
|
|
|
Args:
|
|
fix: If True, ask the linter to fix what errors it can automatically.
|
|
force: Run linter checks even if the inputs have not changed.
|
|
|
|
Returns:
|
|
True on success; False on failure.
|
|
"""
|
|
deps = self.source_files + [self.config_path]
|
|
if not force and not _must_build(self.output, deps):
|
|
return True
|
|
# Windows shows an error when the file location has '\' .
|
|
if sys.platform == 'win32':
|
|
self.config_path = self.config_path.replace('\\', '/')
|
|
self.source_files = [f.replace('\\', '/') for f in self.source_files]
|
|
|
|
stylelint = shakaBuildHelpers.get_node_binary('stylelint')
|
|
cmd_line = stylelint + [
|
|
'--config', self.config_path,
|
|
# The "default ignores" is something like **/node_modules/**, which
|
|
# means that if we run the build scripts from inside the installed node
|
|
# modules of shaka-player, all our sources will be filtered out if we
|
|
# don't disable the default ignores in stylelint.
|
|
'--disable-default-ignores',
|
|
] + self.source_files
|
|
|
|
if fix:
|
|
cmd_line += ['--fix']
|
|
|
|
if shakaBuildHelpers.execute_get_code(cmd_line) != 0:
|
|
return False
|
|
|
|
# Update the timestamp of the file that tracks when we last updated.
|
|
_update_timestamp(self.output)
|
|
return True
|
|
|
|
|
|
class HtmlLinter(object):
|
|
def __init__(self, source_files, config_path):
|
|
self.source_files = _canonicalize_source_files(source_files)
|
|
self.config_path = config_path
|
|
self.output = _get_source_path('dist/.htmllintstamp')
|
|
|
|
def lint(self, force=False):
|
|
"""Run HTML linter checks on the files in |self.source_files|.
|
|
|
|
Args:
|
|
force: Run linter checks even if the inputs have not changed.
|
|
|
|
Returns:
|
|
True on success; False on failure.
|
|
"""
|
|
deps = self.source_files + [self.config_path]
|
|
if not force and not _must_build(self.output, deps):
|
|
return True
|
|
|
|
htmlhint = shakaBuildHelpers.get_node_binary('htmlhint')
|
|
cmd_line = htmlhint + ['--config=' + self.config_path] + self.source_files
|
|
|
|
if shakaBuildHelpers.execute_get_code(cmd_line) != 0:
|
|
return False
|
|
|
|
# Update the timestamp of the file that tracks when we last updated.
|
|
_update_timestamp(self.output)
|
|
return True
|
|
|
|
|
|
class Jsdoc(object):
|
|
def __init__(self, config_path):
|
|
self.config_path = config_path
|
|
self.source_files = shakaBuildHelpers.get_all_files(
|
|
_get_source_path('docs/tutorials'))
|
|
self.source_files += shakaBuildHelpers.get_all_files(
|
|
_get_source_path('docs/jsdoc-template'))
|
|
self.source_files += [
|
|
_get_source_path('docs/jsdoc-plugin.js'),
|
|
_get_source_path('docs/api-mainpage.md'),
|
|
]
|
|
|
|
# Just one of many output files, used to check the freshness of the docs.
|
|
self.output = _get_source_path('docs/api/index.html')
|
|
|
|
# To avoid getting out of sync with the source files jsdoc actually reads,
|
|
# parse the config file and locate all source files based on that.
|
|
with open(self.config_path, 'r') as f:
|
|
config = json.load(f)
|
|
for path in config['source']['include']:
|
|
full_path = _get_source_path(path)
|
|
self.source_files += shakaBuildHelpers.get_all_js_files(full_path)
|
|
|
|
def build(self, force=False):
|
|
"""Build the documentation.
|
|
|
|
Args:
|
|
force: Build the docs even if the inputs have not changed.
|
|
|
|
Returns:
|
|
True on success; False on failure.
|
|
"""
|
|
deps = self.source_files + [self.config_path]
|
|
if not force and not _must_build(self.output, deps):
|
|
return True
|
|
|
|
base = _get_source_path('')
|
|
|
|
# Wipe out any old docs.
|
|
shutil.rmtree(os.path.join(base, 'docs', 'api'), ignore_errors=True)
|
|
|
|
# Jsdoc expects to run from the base dir.
|
|
with shakaBuildHelpers.InDir(base):
|
|
jsdoc = shakaBuildHelpers.get_node_binary('jsdoc')
|
|
cmd_line = jsdoc + ['-c', self.config_path]
|
|
if shakaBuildHelpers.execute_get_code(cmd_line) != 0:
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
class GenerateLocalizations(object):
|
|
def __init__(self, locales):
|
|
self.locales = locales
|
|
self.source_files = shakaBuildHelpers.get_all_files(
|
|
_get_source_path('ui/locales'))
|
|
self.output = _get_source_path('dist/locales.js')
|
|
|
|
def _locales_changed(self):
|
|
# If locales is None, it means we are being called by a caller who doesn't
|
|
# care what locales are in use. This is true, for example, when we are
|
|
# running a compiler pass over the tests.
|
|
if self.locales is None:
|
|
return False
|
|
|
|
# Find out what locales we used before. If they have changed, we must
|
|
# regenerate the output.
|
|
last_locales = None
|
|
try:
|
|
prefix = '// LOCALES: '
|
|
with shakaBuildHelpers.open_file(self.output, 'r') as f:
|
|
for line in f:
|
|
if line.startswith(prefix):
|
|
last_locales = line.replace(prefix, '').strip().split(', ')
|
|
except IOError:
|
|
# The file wasn't found or couldn't be read, so it needs to be redone.
|
|
return True
|
|
|
|
return set(last_locales) != set(self.locales)
|
|
|
|
def generate(self, force=False):
|
|
"""Generate runtime localizations.
|
|
|
|
Args:
|
|
force: Generate the localizations even if the inputs and locales have not
|
|
changed.
|
|
|
|
Returns:
|
|
True on success; False on failure.
|
|
"""
|
|
|
|
if (not force and not _must_build(self.output, self.source_files) and
|
|
not self._locales_changed()):
|
|
return True
|
|
|
|
locales = self.locales or ['en']
|
|
generateLocalizations.main(['--locales'] + locales)
|
|
return True
|