jib-profiles.js revision 2369:66b770c85156
1/*
2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * This file defines build profiles for the JIB tool and others.
28 *
29 * A build profile defines a set of configuration options and external
30 * dependencies that we for some reason or other care about specifically.
31 * Typically, build profiles are defined for the build configurations we
32 * build regularly.
33 *
34 * Contract against this file from the tools that use it, is to provide
35 * a function on the form:
36 *
37 * getJibProfiles(input)
38 *
39 * which returns an object graph describing the profiles and their
40 * dependencies. The name of the function is based on the name of this
41 * file, minus the extension and the '-', camel cased and prefixed with
42 * 'get'.
43 *
44 *
45 * The parameter 'input' is an object that optionally contains  some data.
46 * Optionally because a tool may read the configuration for different purposes.
47 * To initially get a list of available profiles, the active profile may not
48 * yet be known for instance.
49 *
50 * Data that may be set on the input object:
51 *
52 * input.profile = <name of active profile>
53 *
54 * If the active profile is set, the following data from it must also
55 * be provided:
56 *
57 * input.profile
58 * input.target_os
59 * input.target_cpu
60 * input.build_os
61 * input.build_cpu
62 * input.target_platform
63 * input.build_platform
64 * // The build_osenv_* variables describe the unix layer on Windows systems,
65 * // i.e. Cygwin, which may also be 32 or 64 bit.
66 * input.build_osenv
67 * input.build_osenv_cpu
68 * input.build_osenv_platform
69 *
70 * For more complex nested attributes, there is a method "get":
71 *
72 * input.get("<dependency>", "<attribute>")
73 *
74 * Valid attributes are:
75 * install_path
76 * download_path
77 * download_dir
78 *
79 *
80 * The output data generated by this configuration file has the following
81 * format:
82 *
83 * data: {
84 *   // Identifies the version of this format to the tool reading it
85 *   format_version: "1.0",
86 *
87 *   // Name of base outputdir. JIB assumes the actual output dir is formed
88 *   // by adding the configuration name: <output_basedir>/<config-name>
89 *   output_basedir: "build",
90 *   // Configure argument to use to specify configuration name
91 *   configuration_configure_arg:
92 *   // Make argument to use to specify configuration name
93 *   configuration_make_arg:
94 *
95 *   profiles: {
96 *     <profile-name>: {
97 *       // Name of os the profile is built to run on
98 *       target_os; <string>
99 *       // Name of cpu the profile is built to run on
100 *       target_cpu; <string>
101 *       // Combination of target_os and target_cpu for convenience
102 *       target_platform; <string>
103 *       // Name of os the profile is built on
104 *       build_os; <string>
105 *       // Name of cpu the profile is built on
106 *       build_cpu; <string>
107 *       // Combination of build_os and build_cpu for convenience
108 *       build_platform; <string>
109 *
110 *       // List of dependencies needed to build this profile
111 *       dependencies: <Array of strings>
112 *
113 *       // List of configure args to use for this profile
114 *       configure_args: <Array of strings>
115 *
116 *       // List of free form labels describing aspects of this profile
117 *       labels: <Array of strings>
118 *     }
119 *   }
120 *
121 *   // Dependencies use a Maven like deployment structure
122 *   dependencies: {
123 *     <dependency-name>: {
124 *       // Organization part of path defining this dependency
125 *       organization: <string>
126 *       // File extension for this dependency
127 *       ext: <string>
128 *       // Module part of path for defining this dependency,
129 *       // defaults to <dependency-name>
130 *       module: <string>
131 *       // Revision part of path for defining this dependency
132 *       revision: <string>
133 *
134 *       // List of configure args to add when using this dependency,
135 *       // defaults to
136 *       // "--with-<dependency-name>=input.get("<dependency-name", "install_path")"
137 *       configure_args: <array of strings>
138 *
139 *       // Name of environment variable to set when using this dependency
140 *       // when running make
141 *       environment_name: <string>
142 *       // Value of environment variable to set when using this dependency
143 *       // when running make
144 *       environment_value: <string>
145 *
146 *       // Value to add to the PATH variable when using this dependency,
147 *       // applies to both make and configure
148 *       environment_path: <string>
149 *     }
150 *
151 *     <dependency-name>: {
152 *       // For certain dependencies where a legacy distribution mechanism is
153 *       // already in place, the "javare" server layout is also supported
154 *       // Indicate that an alternate server source and layout should be used
155 *       server: "javare"
156 *
157 *       // For "javare", a combination of module, revision,
158 *       // build number (optional), files and checksum file is possible for
159 *       // artifacts following the standard layout.
160 *       module: <string>
161 *       revision: <string>
162 *       build_number: <string>
163 *       checksum_file: <string>
164 *       file: <string>
165 *
166 *       // For other files, use checksum path and path instead
167 *       checksum_path: <string>
168 *       path: <string>
169 *     }
170 *   }
171 * }
172 */
173
174/**
175 * Main entry to generate the profile configuration
176 *
177 * @param input External data to use for generating the configuration
178 * @returns {{}} Profile configuration
179 */
180var getJibProfiles = function (input) {
181
182    var data = {};
183
184    // Identifies the version of this format to the tool reading it
185    data.format_version = "1.0";
186
187    // Organization is used when uploading/publishing build results
188    data.organization = "com.oracle.jpg.jdk";
189
190    // The base directory for the build output. JIB will assume that the
191    // actual build directory will be <output_basedir>/<configuration>
192    data.output_basedir = "build";
193    // The configure argument to use to specify the name of the configuration
194    data.configuration_configure_arg = "--with-conf-name=";
195    // The make argument to use to specify the name of the configuration
196    data.configuration_make_arg = "CONF_NAME=";
197
198    // Define some common values
199    var common = getJibProfilesCommon(input);
200    // Generate the profiles part of the configuration
201    data.profiles = getJibProfilesProfiles(input, common);
202    // Generate the dependencies part of the configuration
203    data.dependencies = getJibProfilesDependencies(input, common);
204
205    return data;
206};
207
208/**
209 * Generates some common values
210 *
211 * @param input External data to use for generating the configuration
212 * @returns Common values
213 */
214var getJibProfilesCommon = function (input) {
215    var common = {};
216
217    common.dependencies = ["boot_jdk", "gnumake", "jtreg"],
218    common.default_make_targets = ["product-bundles", "test-bundles"],
219    common.default_make_targets_debug = common.default_make_targets;
220    common.default_make_targets_slowdebug = common.default_make_targets;
221    common.configure_args = ["--enable-jtreg-failure-handler"],
222    common.configure_args_32bit = ["--with-target-bits=32"],
223    common.configure_args_debug = ["--enable-debug"],
224    common.configure_args_slowdebug = ["--with-debug-level=slowdebug"],
225    common.organization = "jpg.infra.builddeps"
226
227    var boot_jdk_revision = "8";
228    var boot_jdk_subdirpart = "1.8.0";
229    // JDK 8 does not work on sparc M7 cpus, need a newer update when building
230    // on such hardware.
231    if (input.build_cpu == "sparcv9") {
232       var cpu_brand = $EXEC("bash -c \"kstat -m cpu_info | grep brand | head -n1 | awk '{ print \$2 }'\"");
233       if (cpu_brand.trim() == 'SPARC-M7') {
234           boot_jdk_revision = "8u20";
235           boot_jdk_subdirpart = "1.8.0_20";
236       }
237    }
238    common.boot_jdk_revision = boot_jdk_revision;
239    common.boot_jdk_subdirpart = boot_jdk_subdirpart;
240    common.boot_jdk_home = input.get("boot_jdk", "home_path") + "/jdk"
241        + common.boot_jdk_subdirpart
242        + (input.build_os == "macosx" ? ".jdk/Contents/Home" : "");
243
244    return common;
245};
246
247/**
248 * Generates the profiles part of the configuration.
249 *
250 * @param input External data to use for generating the configuration
251 * @param common The common values
252 * @returns {{}} Profiles part of the configuration
253 */
254var getJibProfilesProfiles = function (input, common) {
255    var profiles = {};
256
257    // Main SE profiles
258    var mainProfiles = {
259
260        "linux-x64": {
261            target_os: "linux",
262            target_cpu: "x64",
263            dependencies: concat(common.dependencies, "devkit"),
264            configure_args: concat(common.configure_args, "--with-zlib=system"),
265            default_make_targets: concat(common.default_make_targets, "docs-bundles")
266        },
267
268        "linux-x86": {
269            target_os: "linux",
270            target_cpu: "x86",
271            build_cpu: "x64",
272            dependencies: concat(common.dependencies, "devkit"),
273            configure_args: concat(common.configure_args, common.configure_args_32bit,
274                "--with-jvm-variants=minimal,server", "--with-zlib=system"),
275            default_make_targets: common.default_make_targets
276        },
277
278        "macosx-x64": {
279            target_os: "macosx",
280            target_cpu: "x64",
281            dependencies: concat(common.dependencies, "devkit"),
282            configure_args: concat(common.configure_args, "--with-zlib=system"),
283            default_make_targets: common.default_make_targets
284        },
285
286        "solaris-x64": {
287            target_os: "solaris",
288            target_cpu: "x64",
289            dependencies: concat(common.dependencies, "devkit", "cups"),
290            configure_args: concat(common.configure_args, "--with-zlib=system",
291                "--enable-dtrace"),
292            default_make_targets: common.default_make_targets
293        },
294
295        "solaris-sparcv9": {
296            target_os: "solaris",
297            target_cpu: "sparcv9",
298            dependencies: concat(common.dependencies, "devkit", "cups"),
299            configure_args: concat(common.configure_args, "--with-zlib=system",
300                "--enable-dtrace"),
301            default_make_targets: common.default_make_targets
302        },
303
304        "windows-x64": {
305            target_os: "windows",
306            target_cpu: "x64",
307            dependencies: concat(common.dependencies, "devkit", "freetype"),
308            configure_args: concat(common.configure_args),
309            default_make_targets: common.default_make_targets
310        },
311
312        "windows-x86": {
313            target_os: "windows",
314            target_cpu: "x86",
315            build_cpu: "x64",
316            dependencies: concat(common.dependencies, "devkit", "freetype"),
317            configure_args: concat(common.configure_args, common.configure_args_32bit),
318            default_make_targets: common.default_make_targets
319        }
320    };
321    profiles = concatObjects(profiles, mainProfiles);
322    // Generate debug versions of all the main profiles
323    profiles = concatObjects(profiles, generateDebugProfiles(common, mainProfiles));
324    // Generate slowdebug versions of all the main profiles
325    profiles = concatObjects(profiles, generateSlowdebugProfiles(common, mainProfiles));
326
327    // Generate open only profiles for all the main profiles for JPRT and reference
328    // implementation builds.
329    var openOnlyProfiles = generateOpenOnlyProfiles(common, mainProfiles);
330    // The open only profiles on linux are used for reference builds and should
331    // produce the compact profile images by default. This adds "profiles" as an
332    // extra default target.
333    var openOnlyProfilesExtra = {
334        "linux-x64-open": {
335            default_make_targets: "profiles"
336        },
337
338        "linux-x86-open": {
339            default_make_targets: "profiles",
340            configure_args: "--with-jvm-variants=client,server"
341        }
342    };
343    var openOnlyProfiles = concatObjects(openOnlyProfiles, openOnlyProfilesExtra);
344
345    profiles = concatObjects(profiles, openOnlyProfiles);
346    // Generate debug profiles for the open jprt profiles
347    profiles = concatObjects(profiles, generateDebugProfiles(common, openOnlyProfiles));
348
349    // Profiles for building the zero jvm variant. These are used for verification
350    // in JPRT.
351    var zeroProfiles = {
352        "linux-x64-zero": {
353            target_os: "linux",
354            target_cpu: "x64",
355            dependencies: concat(common.dependencies, "devkit"),
356            configure_args: concat(common.configure_args,
357                "--with-zlib=system",
358                "--with-jvm-variants=zero",
359                "--enable-libffi-bundling"),
360            default_make_targets: common.default_make_targets
361        },
362
363        "linux-x86-zero": {
364            target_os: "linux",
365            target_cpu: "x86",
366            build_cpu: "x64",
367            dependencies: concat(common.dependencies, "devkit"),
368            configure_args: concat(common.configure_args, common.configure_args_32bit,
369                "--with-zlib=system",
370                "--with-jvm-variants=zero",
371                "--enable-libffi-bundling"),
372            default_make_targets: common.default_make_targets
373        },
374    }
375    profiles = concatObjects(profiles, zeroProfiles);
376    profiles = concatObjects(profiles, generateDebugProfiles(common, zeroProfiles));
377
378    // Profiles used to run tests. Used in JPRT.
379    var testOnlyProfiles = {
380
381        "run-test": {
382            target_os: input.build_os,
383            target_cpu: input.build_cpu,
384            dependencies: [ "jtreg", "gnumake", "boot_jdk" ],
385            labels: "test",
386            environment: {
387                "JT_JAVA": common.boot_jdk_home
388            }
389        }
390    };
391    profiles = concatObjects(profiles, testOnlyProfiles);
392
393    // Generate the missing platform attributes
394    profiles = generatePlatformAttributes(profiles);
395    profiles = generateDefaultMakeTargetsConfigureArg(common, profiles);
396    return profiles;
397};
398
399/**
400 * Generate the dependencies part of the configuration
401 *
402 * @param input External data to use for generating the configuration
403 * @param common The common values
404 * @returns {{}} Dependencies part of configuration
405 */
406var getJibProfilesDependencies = function (input, common) {
407
408    var boot_jdk_platform = input.build_os + "-"
409        + (input.build_cpu == "x86" ? "i586" : input.build_cpu);
410
411    var devkit_platform_revisions = {
412        linux_x64: "gcc4.9.2-OEL6.4+1.1",
413        macosx_x64: "Xcode6.3-MacOSX10.9+1.0",
414        solaris_x64: "SS12u4-Solaris11u1+1.0",
415        solaris_sparcv9: "SS12u4-Solaris11u1+1.0",
416        windows_x64: "VS2013SP4+1.0"
417    };
418
419    var devkit_platform = (input.target_cpu == "x86"
420        ? input.target_os + "_x64"
421        : input.target_platform);
422
423    var dependencies = {
424
425        boot_jdk: {
426            server: "javare",
427            module: "jdk",
428            revision: common.boot_jdk_revision,
429            checksum_file: boot_jdk_platform + "/MD5_VALUES",
430            file: boot_jdk_platform + "/jdk-" + common.boot_jdk_revision
431                + "-" + boot_jdk_platform + ".tar.gz",
432            configure_args: "--with-boot-jdk=" + common.boot_jdk_home,
433            environment_path: common.boot_jdk_home
434        },
435
436        devkit: {
437            organization: common.organization,
438            ext: "tar.gz",
439            module: "devkit-" + devkit_platform,
440            revision: devkit_platform_revisions[devkit_platform]
441        },
442
443        build_devkit: {
444            organization: common.organization,
445            ext: "tar.gz",
446            module: "devkit-" + input.build_platform,
447            revision: devkit_platform_revisions[input.build_platform]
448        },
449
450        cups: {
451            organization: common.organization,
452            ext: "tar.gz",
453            revision: "1.0118+1.0"
454        },
455
456        jtreg: {
457            server: "javare",
458            revision: "4.2",
459            build_number: "b04",
460            checksum_file: "MD5_VALUES",
461            file: "jtreg_bin-4.2.zip",
462            environment_name: "JT_HOME",
463            environment_path: input.get("jtreg", "install_path") + "/jtreg/bin"
464        },
465
466        gnumake: {
467            organization: common.organization,
468            ext: "tar.gz",
469            revision: "4.0+1.0",
470
471            module: (input.build_os == "windows"
472                ? "gnumake-" + input.build_osenv_platform
473                : "gnumake-" + input.build_platform),
474
475            configure_args: (input.build_os == "windows"
476                ? "MAKE=" + input.get("gnumake", "install_path") + "/cygwin/bin/make"
477                : "MAKE=" + input.get("gnumake", "install_path") + "/bin/make"),
478
479            environment_path: (input.build_os == "windows"
480                ? input.get("gnumake", "install_path") + "/cygwin/bin"
481                : input.get("gnumake", "install_path") + "/bin")
482        },
483
484        freetype: {
485            organization: common.organization,
486            ext: "tar.gz",
487            revision: "2.3.4+1.0",
488            module: "freetype-" + input.target_platform
489        }
490    };
491
492    return dependencies;
493};
494
495/**
496 * Generate the missing platform attributes for profiles
497 *
498 * @param profiles Profiles map to generate attributes on
499 * @returns {{}} New profiles map with platform attributes fully filled in
500 */
501var generatePlatformAttributes = function (profiles) {
502    var ret = concatObjects(profiles, {});
503    for (var profile in profiles) {
504        if (ret[profile].build_os == null) {
505            ret[profile].build_os = ret[profile].target_os;
506        }
507        if (ret[profile].build_cpu == null) {
508            ret[profile].build_cpu = ret[profile].target_cpu;
509        }
510        ret[profile].target_platform = ret[profile].target_os + "_" + ret[profile].target_cpu;
511        ret[profile].build_platform = ret[profile].build_os + "_" + ret[profile].build_cpu;
512    }
513    return ret;
514};
515
516/**
517 * Generates debug versions of profiles. Clones the given profiles and adds
518 * debug metadata.
519 *
520 * @param common Common values
521 * @param profiles Profiles map to generate debug profiles for
522 * @returns {{}} New map of profiles containing debug profiles
523 */
524var generateDebugProfiles = function (common, profiles) {
525    var newProfiles = {};
526    for (var profile in profiles) {
527        var debugProfile = profile + "-debug";
528        newProfiles[debugProfile] = clone(profiles[profile]);
529        newProfiles[debugProfile].debug_level = "fastdebug";
530        newProfiles[debugProfile].default_make_targets
531            = common.default_make_targets_debug;
532        newProfiles[debugProfile].labels
533            = concat(newProfiles[debugProfile].labels || [], "debug"),
534            newProfiles[debugProfile].configure_args
535                = concat(newProfiles[debugProfile].configure_args,
536                common.configure_args_debug);
537    }
538    return newProfiles;
539};
540
541/**
542 * Generates slowdebug versions of profiles. Clones the given profiles and adds
543 * debug metadata.
544 *
545 * @param common Common values
546 * @param profiles Profiles map to generate debug profiles for
547 * @returns {{}} New map of profiles containing debug profiles
548 */
549var generateSlowdebugProfiles = function (common, profiles) {
550    var newProfiles = {};
551    for (var profile in profiles) {
552        var debugProfile = profile + "-slowdebug";
553        newProfiles[debugProfile] = clone(profiles[profile]);
554        newProfiles[debugProfile].debug_level = "slowdebug";
555        newProfiles[debugProfile].default_make_targets
556            = common.default_make_targets_slowdebug;
557        newProfiles[debugProfile].labels
558            = concat(newProfiles[debugProfile].labels || [], "slowdebug"),
559            newProfiles[debugProfile].configure_args
560                = concat(newProfiles[debugProfile].configure_args,
561                common.configure_args_slowdebug);
562    }
563    return newProfiles;
564};
565
566/**
567 * Generates open only versions of profiles. Clones the given profiles and adds
568 * open metadata.
569 *
570 * @param common Common values
571 * @param profiles Profiles map to generate open only profiles for
572 * @returns {{}} New map of profiles containing open only profiles
573 */
574var generateOpenOnlyProfiles = function (common, profiles) {
575    var newProfiles = {};
576    for (var profile in profiles) {
577        var openProfile = profile + "-open";
578        newProfiles[openProfile] = clone(profiles[profile]);
579        newProfiles[openProfile].labels
580            = concat(newProfiles[openProfile].labels || [], "open"),
581            newProfiles[openProfile].configure_args
582                = concat(newProfiles[openProfile].configure_args,
583                "--enable-openjdk-only");
584    }
585    return newProfiles;
586};
587
588/**
589 * The default_make_targets attribute on a profile is not a real Jib attribute.
590 * This function rewrites that attribute into the corresponding configure arg.
591 * Calling this function multiple times on the same profiles object is safe.
592 *
593 * @param common Common values
594 * @param profiles Profiles map to rewrite profiles for
595 * @returns {{}} New map of profiles with the make targets converted
596 */
597var generateDefaultMakeTargetsConfigureArg = function (common, profiles) {
598    var ret = concatObjects(profiles, {});
599    for (var profile in ret) {
600        if (ret[profile]["default_make_targets"] != null) {
601            var targetsString = concat(ret[profile].default_make_targets).join(" ");
602            // Iterate over all configure args and see if --with-default-make-target
603            // is already there and change it, otherwise add it.
604            var found = false;
605            for (var arg in ret[profile].configure_args) {
606                if (arg.startsWith("--with-default-make-target")) {
607                    found = true;
608                    arg.replace(/=.*/, "=" + targetsString);
609                }
610            }
611            if (!found) {
612                ret[profile].configure_args = concat(
613                    ret[profile].configure_args,
614                    "--with-default-make-target=" + targetsString);
615            }
616        }
617    }
618    return ret;
619}
620
621/**
622 * Deep clones an object tree.
623 *
624 * @param o Object to clone
625 * @returns {{}} Clone of o
626 */
627var clone = function (o) {
628    return JSON.parse(JSON.stringify(o));
629};
630
631/**
632 * Concatenates all arguments into a new array
633 *
634 * @returns {Array.<T>} New array containing all arguments
635 */
636var concat = function () {
637    return Array.prototype.concat.apply([], arguments);
638};
639
640/**
641 * Copies all elements in an array into a new array but replacing all
642 * occurrences of original with replacement.
643 *
644 * @param original Element to look for
645 * @param replacement Element to replace with
646 * @param a Array to copy
647 * @returns {Array} New array with all occurrences of original replaced
648 *                  with replacement
649 */
650var replace = function (original, replacement, a) {
651    var newA = [];
652    for (var i in a) {
653        if (original == a[i]) {
654            newA.push(replacement);
655        } else {
656            newA.push(a[i]);
657        }
658    }
659    return newA;
660};
661
662/**
663 * Deep concatenation of two objects. For each node encountered, merge
664 * the contents with the corresponding node in the other object tree,
665 * treating all strings as array elements.
666 *
667 * @param o1 Object to concatenate
668 * @param o2 Object to concatenate
669 * @returns {{}} New object tree containing the concatenation of o1 and o2
670 */
671var concatObjects = function (o1, o2) {
672    var ret = {};
673    for (var a in o1) {
674        if (o2[a] == null) {
675            ret[a] = o1[a];
676        }
677    }
678    for (var a in o2) {
679        if (o1[a] == null) {
680            ret[a] = o2[a];
681        } else {
682            if (typeof o1[a] == 'string') {
683                ret[a] = [o1[a]].concat(o2[a]);
684            } else if (Array.isArray(o1[a])) {
685                ret[a] = o1[a].concat(o2[a]);
686            } else if (typeof o1[a] == 'object') {
687                ret[a] = concatObjects(o1[a], o2[a]);
688            }
689        }
690    }
691    return ret;
692};
693