Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit d56d0e7

Browse files
Splaktarmmalerba
authored andcommitted
build: make the build output deterministic and reproducible (#11570)
<!-- Filling out this template is required! Do not delete it when submitting a Pull Request! Without this information, your Pull Request may be auto-closed. --> ## PR Checklist Please check that your PR fulfills the following requirements: - [x] The commit message follows [our guidelines](https://github.com/angular/material/blob/master/.github/CONTRIBUTING.md#-commit-message-format) - [x] Tests for the changes have been added or this is not a bug fix / enhancement - [x] Docs have been added, updated, or were not required ## PR Type What kind of change does this PR introduce? <!-- Please check the one that applies to this PR using "x". --> ``` [ ] Bugfix [ ] Enhancement [ ] Documentation content changes [ ] Code style update (formatting, local variables) [ ] Refactoring (no functional changes, no api changes) [x] Build related changes [ ] CI related changes [ ] Infrastructure changes [ ] Other... Please describe: ``` ## What is the current behavior? Consecutive builds, without any changes, result in bundle content with different ordering. This is even true in a single build where the `dist/angular-material.js` isn't identical to the `dist/docs/angular-material.js`. This makes comparing releases harder and it makes syncing into g3 more time consuming. - passing globs to `gulp.src` can cause ordering to not be preserved. this is because `gulp.src` is async - gulp-concat tries to preserve the order passed into `gulp.src`. if the globbing is too vague, then there isn't enough info for ordering <!-- Please describe the current behavior that you are modifying and link to one or more relevant issues. --> Issue Number: Fixes #11502 ## What is the new behavior? All of the build output (JS, SCSS, CSS) should be identical if the build is run multiple times without any changes to the code. ## Does this PR introduce a breaking change? ``` [ ] Yes [x] No ``` <!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. --> <!-- Note that breaking changes are highly unlikely to get merged to master unless the validation is clear and the use case is critical. --> ## Other information - more statically defining the paths like this isn't ideal but it is acceptable atm since we aren't adding new components. I looked at sorting the file paths manually or using `gulp-sort` but neither solution was flexible enough to order some of our internal dependencies properly. This approach seems to work fine w/o adding `gulp-order`, but if problems pop up in the future, that is another package that might help. Note that the root cause of this lies in the intersection of `gulp.src` in Gulp core and `gulp-concat`. This is a well established problem that the Gulp team has mostly decided to punt into user land or third party packages so that they can maintain speed and not need to support crazy levels of customization.
1 parent ddcbb2e commit d56d0e7

File tree

11 files changed

+278
-196
lines changed

11 files changed

+278
-196
lines changed

docs/gulpfile.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@ const argv = require('minimist')(process.argv.slice(2));
1717
const gutil = require('gulp-util');
1818
const series = require('stream-series');
1919

20-
const config = {
21-
demoFolder: 'demo-partials'
22-
};
23-
2420
gulp.task('demos', function() {
2521
const demos = [];
2622
return generateDemos()

gulp/config.js

Lines changed: 115 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,89 @@ module.exports = {
1010
' * @license MIT\n' +
1111
' * v' + VERSION + '\n' +
1212
' */\n',
13-
jsBaseFiles: [
14-
'src/core/**/*.js'
13+
jsCoreFiles: [
14+
'src/core/*.js',
15+
'src/core/util/autofocus.js',
16+
'src/core/util/color.js',
17+
'src/core/util/constant.js',
18+
'src/core/util/iterator.js',
19+
'src/core/util/media.js',
20+
'src/core/util/prefixer.js',
21+
'src/core/util/util.js',
22+
'src/core/util/animation/animate.js',
23+
'src/core/util/animation/animateCss.js',
24+
'src/core/services/aria/*.js',
25+
'src/core/services/compiler/*.js',
26+
'src/core/services/gesture/*.js',
27+
'src/core/services/interaction/*.js',
28+
'src/core/services/interimElement/*.js',
29+
'src/core/services/layout/*.js',
30+
'src/core/services/liveAnnouncer/*.js',
31+
'src/core/services/meta/*.js',
32+
'src/core/services/registry/*.js',
33+
'src/core/services/ripple/button_ripple.js',
34+
'src/core/services/ripple/checkbox_ripple.js',
35+
'src/core/services/ripple/list_ripple.js',
36+
'src/core/services/ripple/ripple.js',
37+
'src/core/services/ripple/tab_ripple.js',
38+
'src/core/services/theming/theme.palette.js',
39+
'src/core/services/theming/theming.js'
1540
],
16-
jsFiles: [
41+
jsHintFiles: [
1742
'src/**/*.js',
1843
'!src/**/*.spec.js'
1944
],
20-
mockFiles : [
45+
componentPaths: [
46+
'src/components/autocomplete',
47+
'src/components/backdrop',
48+
'src/components/bottomSheet',
49+
'src/components/button',
50+
'src/components/card',
51+
'src/components/checkbox',
52+
'src/components/chips',
53+
'src/components/colors',
54+
'src/components/content',
55+
'src/components/datepicker',
56+
'src/components/dialog',
57+
'src/components/divider',
58+
'src/components/fabActions',
59+
'src/components/fabSpeedDial',
60+
'src/components/fabToolbar',
61+
'src/components/gridList',
62+
'src/components/icon',
63+
'src/components/input',
64+
'src/components/list',
65+
'src/components/menu',
66+
'src/components/menuBar',
67+
'src/components/navBar',
68+
'src/components/panel',
69+
'src/components/progressCircular',
70+
'src/components/progressLinear',
71+
'src/components/radioButton',
72+
'src/components/select',
73+
'src/components/showHide',
74+
'src/components/sidenav',
75+
'src/components/slider',
76+
'src/components/sticky',
77+
'src/components/subheader',
78+
'src/components/swipe',
79+
'src/components/switch',
80+
'src/components/tabs',
81+
'src/components/toast',
82+
'src/components/toolbar',
83+
'src/components/tooltip',
84+
'src/components/truncate',
85+
'src/components/virtualRepeat',
86+
'src/components/whiteframe'
87+
],
88+
mockFiles: [
2189
'test/angular-material-mocks.js'
2290
],
2391
themeBaseFiles: [
2492
'src/core/style/variables.scss',
2593
'src/core/style/mixins.scss'
2694
],
95+
themeCore: 'src/core/style/core-theme.scss',
2796
scssBaseFiles: [
2897
'src/core/style/color-palette.scss',
2998
'src/core/style/variables.scss',
@@ -33,6 +102,7 @@ module.exports = {
33102
'src/core/style/layout.scss',
34103
'src/components/panel/*.scss'
35104
],
105+
scssServicesLayout: 'src/core/services/layout/layout.scss',
36106
scssLayoutFiles: [
37107
'src/core/style/variables.scss',
38108
'src/core/style/mixins.scss',
@@ -44,14 +114,46 @@ module.exports = {
44114
'src/core/style/mixins.scss',
45115
'src/core/services/layout/layout-attributes.scss'
46116
],
47-
scssPaths : [
48-
'src/components/**/*.scss',
49-
'src/core/services/**/*.scss'
117+
scssComponentPaths: [
118+
'src/components/autocomplete',
119+
'src/components/backdrop',
120+
'src/components/bottomSheet',
121+
'src/components/button',
122+
'src/components/card',
123+
'src/components/checkbox',
124+
'src/components/chips',
125+
'src/components/content',
126+
'src/components/datepicker',
127+
'src/components/dialog',
128+
'src/components/divider',
129+
'src/components/fabSpeedDial',
130+
'src/components/fabToolbar',
131+
'src/components/gridList',
132+
'src/components/icon',
133+
'src/components/input',
134+
'src/components/list',
135+
'src/components/menu',
136+
'src/components/menuBar',
137+
'src/components/navBar',
138+
// panel is included in scssBaseFiles above
139+
'src/components/progressCircular',
140+
'src/components/progressLinear',
141+
'src/components/radioButton',
142+
'src/components/select',
143+
'src/components/sidenav',
144+
'src/components/slider',
145+
'src/components/sticky',
146+
'src/components/subheader',
147+
'src/components/swipe',
148+
'src/components/switch',
149+
'src/components/tabs',
150+
'src/components/toast',
151+
'src/components/toolbar',
152+
'src/components/tooltip',
153+
'src/components/truncate',
154+
'src/components/virtualRepeat',
155+
'src/components/whiteframe'
50156
],
51-
cssIEPaths : ['src/**/ie_fixes.css'],
52-
paths: 'src/+(components|core)/**',
53-
outputDir: 'dist/',
54-
demoFolder: 'demo-partials'
157+
cssIEPaths: ['src/**/ie_fixes.css'],
158+
outputDir: 'dist/'
55159
};
56-
57-

gulp/tasks/build-demo.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ const util = require('../util');
33
exports.dependencies = ['build', 'build-module-demo'];
44

55
exports.task = function() {
6-
return util.buildModule(util.readModuleArg(), false);
6+
return util.buildModule(util.readModuleArg());
77
};

gulp/tasks/build-js.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const util = require('../util');
22

33
exports.task = function() {
4-
return util.buildJs(true);
4+
return util.buildJs();
55
};

gulp/tasks/build-module-demo.js

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,23 @@ exports.task = function() {
1818
const mod = util.readModuleArg();
1919
const name = mod.split('.').pop();
2020
const demoIndexTemplate = fs.readFileSync(
21-
ROOT + '/docs/config/template/demo-index.template.html', 'utf8'
21+
ROOT + '/docs/config/template/demo-index.template.html', 'utf8'
2222
).toString();
2323

2424
gutil.log('Building demos for', mod, '...');
2525

2626
return utils.readModuleDemos(mod, function() {
2727
return lazypipe()
28-
.pipe(gulpif, /.css$/, sass())
29-
.pipe(gulpif, /.css$/, util.autoprefix())
30-
.pipe(gulp.dest, BUILD_MODE.outputDir + name)
28+
.pipe(gulpif, /.css$/, sass())
29+
.pipe(gulpif, /.css$/, util.autoprefix())
30+
.pipe(gulp.dest, BUILD_MODE.outputDir + name)
3131
();
3232
})
33-
.pipe(through2.obj(function(demo, enc, next) {
34-
fs.writeFileSync(
35-
path.resolve(BUILD_MODE.outputDir, name, demo.name, 'index.html'),
36-
_.template(demoIndexTemplate)(demo)
37-
);
38-
next();
39-
}));
40-
33+
.pipe(through2.obj(function(demo, enc, next) {
34+
fs.writeFileSync(
35+
path.resolve(BUILD_MODE.outputDir, name, demo.name, 'index.html'),
36+
_.template(demoIndexTemplate)(demo)
37+
);
38+
next();
39+
}));
4140
};

gulp/tasks/build-scss.js

Lines changed: 60 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -13,101 +13,104 @@ const gulpif = require('gulp-if');
1313
const minifyCss = util.minifyCss;
1414
const args = util.args;
1515
const IS_DEV = require('../const').IS_DEV;
16+
const path = require('path');
1617

1718
exports.task = function() {
1819
const streams = [];
19-
const modules = args.modules,
20-
overrides = args.override,
21-
dest = args['output-dir'] || config.outputDir,
22-
layoutDest= dest + 'layouts/';
20+
const modules = args.modules,
21+
overrides = args.override,
22+
dest = args['output-dir'] || config.outputDir,
23+
layoutDest = dest + 'layouts/';
2324

2425
gutil.log("Building css files...");
2526

2627
// create SCSS file for distribution
2728
streams.push(
2829
gulp.src(getPaths())
29-
.pipe(util.filterNonCodeFiles())
30-
.pipe(filter(['**', '!**/*.css']))
31-
.pipe(filter(['**', '!**/*-theme.scss']))
32-
.pipe(filter(['**', '!**/*-attributes.scss']))
33-
.pipe(concat('angular-material.scss'))
34-
.pipe(gulp.dest(dest)) // raw uncompiled SCSS
35-
.pipe(sass())
36-
.pipe(util.dedupeCss())
37-
.pipe(util.autoprefix())
38-
.pipe(insert.prepend(config.banner))
39-
.pipe(gulp.dest(dest)) // unminified
40-
.pipe(gulpif(!IS_DEV, minifyCss()))
41-
.pipe(gulpif(!IS_DEV, util.dedupeCss()))
42-
.pipe(rename({extname: '.min.css'}))
43-
.pipe(gulp.dest(dest)) // minified
30+
.pipe(util.filterNonCodeFiles())
31+
.pipe(filter(['**', '!**/*.css']))
32+
.pipe(filter(['**', '!**/*-theme.scss']))
33+
.pipe(filter(['**', '!**/*-attributes.scss']))
34+
.pipe(concat('angular-material.scss'))
35+
.pipe(insert.prepend(config.banner))
36+
.pipe(gulp.dest(dest)) // raw uncompiled SCSS
37+
.pipe(sass())
38+
.pipe(util.dedupeCss())
39+
.pipe(util.autoprefix())
40+
.pipe(gulp.dest(dest)) // unminified
41+
.pipe(gulpif(!IS_DEV, minifyCss()))
42+
.pipe(gulpif(!IS_DEV, util.dedupeCss()))
43+
.pipe(rename({extname: '.min.css'}))
44+
.pipe(gulp.dest(dest)) // minified
4445
);
4546

4647
streams.push(
47-
gulp.src(config.cssIEPaths.slice()) // append raw CSS for IE Fixes
48-
.pipe(concat('angular-material.layouts.ie_fixes.css'))
49-
.pipe(gulp.dest(layoutDest))
48+
gulp.src(config.cssIEPaths.slice()) // append raw CSS for IE Fixes
49+
.pipe(concat('angular-material.layouts.ie_fixes.css'))
50+
.pipe(gulp.dest(layoutDest))
5051
);
5152

5253
// Generate standalone SCSS (and CSS) file for Layouts API
5354
// The use of these classnames is automated but requires
54-
// the Javascript module module `material.core.layout`
55+
// the Javascript module `material.core.layout`
5556
// > (see src/core/services/layout.js)
5657
// NOTE: this generated css is ALSO appended to the published
5758
// angular-material.css file
5859

5960
streams.push(
6061
gulp.src(config.scssLayoutFiles)
61-
.pipe(concat('angular-material.layouts.scss'))
62-
.pipe(sassUtils.hoistScssVariables())
63-
.pipe(insert.prepend(config.banner))
64-
.pipe(gulp.dest(layoutDest)) // raw uncompiled SCSS
65-
.pipe(sass())
66-
.pipe(util.dedupeCss())
67-
.pipe(util.autoprefix())
68-
.pipe(rename({ extname : '.css'}))
69-
.pipe(gulp.dest(layoutDest))
70-
.pipe(gulpif(!IS_DEV, minifyCss()))
71-
.pipe(gulpif(!IS_DEV, util.dedupeCss()))
72-
.pipe(rename({extname: '.min.css'}))
73-
.pipe(gulp.dest(layoutDest))
62+
.pipe(concat('angular-material.layouts.scss'))
63+
.pipe(sassUtils.hoistScssVariables())
64+
.pipe(insert.prepend(config.banner))
65+
.pipe(gulp.dest(layoutDest)) // raw uncompiled SCSS
66+
.pipe(sass())
67+
.pipe(util.dedupeCss())
68+
.pipe(util.autoprefix())
69+
.pipe(rename({extname: '.css'}))
70+
.pipe(gulp.dest(layoutDest))
71+
.pipe(gulpif(!IS_DEV, minifyCss()))
72+
.pipe(gulpif(!IS_DEV, util.dedupeCss()))
73+
.pipe(rename({extname: '.min.css'}))
74+
.pipe(gulp.dest(layoutDest))
7475
);
7576

7677
// Generate the Layout-Attributes SCSS and CSS files
7778
// These are intended to allow usages of the Layout styles
7879
// without:
7980
// * use of the Layout directives and classnames, and
80-
// * Layout module `material.core.layout
81+
// * Layout module `material.core.layout`
8182

8283
streams.push(
83-
gulp.src(config.scssLayoutAttributeFiles)
84-
.pipe(concat('angular-material.layout-attributes.scss'))
85-
.pipe(sassUtils.hoistScssVariables())
86-
.pipe(gulp.dest(layoutDest)) // raw uncompiled SCSS
87-
.pipe(sass())
88-
.pipe(util.dedupeCss())
89-
.pipe(util.autoprefix())
90-
.pipe(rename({ extname : '.css'}))
91-
.pipe(insert.prepend(config.banner))
92-
.pipe(gulp.dest(layoutDest))
93-
.pipe(gulpif(!IS_DEV, minifyCss()))
94-
.pipe(gulpif(!IS_DEV, util.dedupeCss()))
95-
.pipe(rename({extname: '.min.css'}))
96-
.pipe(gulp.dest(layoutDest))
84+
gulp.src(config.scssLayoutAttributeFiles)
85+
.pipe(concat('angular-material.layout-attributes.scss'))
86+
.pipe(sassUtils.hoistScssVariables())
87+
.pipe(gulp.dest(layoutDest)) // raw uncompiled SCSS
88+
.pipe(sass())
89+
.pipe(util.dedupeCss())
90+
.pipe(util.autoprefix())
91+
.pipe(rename({extname: '.css'}))
92+
.pipe(insert.prepend(config.banner))
93+
.pipe(gulp.dest(layoutDest))
94+
.pipe(gulpif(!IS_DEV, minifyCss()))
95+
.pipe(gulpif(!IS_DEV, util.dedupeCss()))
96+
.pipe(rename({extname: '.min.css'}))
97+
.pipe(gulp.dest(layoutDest))
9798
);
9899

99100
return series(streams);
100101

101-
102-
function getPaths () {
103-
const paths = config.scssBaseFiles.slice();
102+
/**
103+
* @returns {string[]} array of SCSS file paths used in the build
104+
*/
105+
function getPaths() {
106+
const paths = config.scssBaseFiles.slice(0);
104107
if (modules) {
105-
paths.push.apply(paths, modules.split(',').map(function (module) {
108+
paths.push.apply(paths, modules.split(',').map(function(module) {
106109
return 'src/components/' + module + '/*.scss';
107110
}));
108111
} else {
109-
paths.push('src/components/**/*.scss');
110-
paths.push('src/core/services/layout/**/*.scss');
112+
config.scssComponentPaths.forEach(component => paths.push(path.join(component, '*.scss')));
113+
paths.push(config.scssServicesLayout);
111114
}
112115
overrides && paths.unshift(overrides);
113116
return paths;

gulp/tasks/jshint.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const gulp = require('gulp');
33
const jshint = require('gulp-jshint');
44

55
exports.task = function() {
6-
return gulp.src(config.jsFiles)
6+
return gulp.src(config.jsHintFiles)
77
.pipe(jshint('.jshintrc'))
88
.pipe(jshint.reporter('jshint-summary', {
99
fileColCol: ',bold',

0 commit comments

Comments
 (0)