CINXE.COM
Configuration cache
<!DOCTYPE html> <html lang=""> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="generator" content="Asciidoctor 2.0.23"> <title>Configuration cache</title> <style> /* * Copyright 2018 the original author or authors. * * 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. */ /* Custom Admonition Blocks */ body { --gradle-blue: #209BC4; --gradle-bg-dark: #010002; --gradle-bg-gray: #F8F8F8; --gradle-bg-white: #FFFFFF; --gradle-blue: #209BC4; --gradle-blue-lite: #4DC9C0; --gradle-blue-dark: #1b3262; --gradle-blue-darker: #19274f; --button-gradient-angle: 160deg; --caution-color: #e40046; --caution-on-color: #fff; --important-color: #802392; --important-on-color: #fff; --note-color: #2d7dd2; --note-on-color: #fff; --tip-color: #43b929; --tip-on-color: #fff; --warning-color: #f70; --warning-on-color: #fff; --admonition-background: #fafafa; --doc-icon-filter: invert(14.5%); --rem-base: 18; --black-color: #000; --white-color: #fff; --text-color: #02303A; --title-color: #02303A; --header-color: rgba(0, 0, 0, 0.85); --footer-color: #fff; --footer-other-text-color: #02303; --footer-form-color: #1BA8CB; --code-color: #f7f7f8; --code-text-color: rgba(0, 0, 0, 0.9); --code-link-color: #021274; --num-color: rgba(0, 0, 0, 0.8); --nav-color: #f8f8f7; --table-color: #f7f8f7; --box-shadow-color: rgba(0, 0, 0, .15); --top-header-color: #fff; --footer-white-color: #fff; --footer-text-color: #fff; --quoteblock-color: #7a2518; --menu-burger-color: #fff; --various-border-color: #e7e7e9; } body.dark-theme { --admonition-background: #2a2929; --black-color: #fff; --white-color: #121212; --text-color: #aaa; --title-color: #fff; --header-color: rgba(255, 255, 255, 0.85); --footer-color: #121212; --footer-other-text-color: #ddd; --code-color: #1f1f1f; --code-text-color: rgba(255, 255, 255, 0.9); --code-link-color: #1fafcc; --num-color: #fff; --nav-color: #121212; --table-color: #121212; --box-shadow-color: rgba(255, 255, 255, .15); --top-header-color: #242526; --footer-white-color: #242526; --footer-text-color: #aaa; --footer-form-color: #1BA8CB; --quoteblock-color: #1DA2BD; --menu-burger-color: #242526; --various-border-color: #242526; } @media (prefers-color-scheme: dark) { /* defaults to dark theme */ body { --admonition-background: #2a2929; --black-color: #fff; --white-color: #121212; --text-color: #aaa; --title-color: #fff; --header-color: rgba(255, 255, 255, 0.85); --footer-color: #121212; --footer-other-text-color: #ddd; --code-color: #1f1f1f; --code-text-color: rgba(255, 255, 255, 0.9); --code-link-color: #1fafcc; --num-color: #fff; --nav-color: #121212; --table-color: #121212; --box-shadow-color: rgba(255, 255, 255, .15); --top-header-color: #242526; --footer-white-color: #242526; --footer-text-color: #aaa; --footer-form-color: #1BA8CB; --quoteblock-color: #1DA2BD; --menu-burger-color: #242526; --various-border-color: #242526; } body.light-theme { --gradle-blue: #209BC4; --gradle-bg-dark: #010002; --gradle-bg-gray: #F8F8F8; --gradle-bg-white: #FFFFFF; --gradle-blue: #209BC4; --gradle-blue-lite: #4DC9C0; --gradle-blue-dark: #1b3262; --gradle-blue-darker: #19274f; --button-gradient-angle: 160deg; --caution-color: #e40046; --caution-on-color: #fff; --important-color: #802392; --important-on-color: #fff; --note-color: #2d7dd2; --note-on-color: #fff; --tip-color: #43b929; --tip-on-color: #fff; --warning-color: #f70; --warning-on-color: #fff; --admonition-background: #fafafa; --doc-icon-filter: invert(14.5%); --rem-base: 18; --black-color: #000; --white-color: #fff; --text-color: #02303A; --title-color: #02303A; --header-color: rgba(0, 0, 0, 0.85); --footer-color: #fff; --footer-other-text-color: #02303; --code-color: #f7f7f8; --code-text-color: rgba(0, 0, 0, 0.9); --code-link-color: #021274; --num-color: rgba(0, 0, 0, 0.8); --nav-color: #f8f8f7; --table-color: #f7f8f7; --box-shadow-color: rgba(0, 0, 0, .15); --top-header-color: #fff; --footer-white-color: #fff; --footer-text-color: #fff; --footer-form-color: #1BA8CB; --quoteblock-color: #7a2518; --menu-burger-color: #fff; --various-border-color: #e7e7e9; } } .cls-1 { fill: #02303a; } body.dark-theme { .cls-1 { fill: #fff; } } @media (prefers-color-scheme: dark) { .cls-1 { fill: #fff; } body.light-theme { .cls-1 { fill: #02303a; } } } /* Lato (normal, regular) */ @font-face { font-family: Lato; font-weight: 400; font-style: normal; src: url("https://assets.gradle.com/lato/fonts/lato-normal/lato-normal.woff2") format("woff2"), url("https://assets.gradle.com/lato/fonts/lato-normal/lato-normal.woff") format("woff"); } /* Lato (normal, italic) */ @font-face { font-display: swap; font-family: Lato; font-weight: 400; font-style: italic; src: url("https://assets.gradle.com/lato/fonts/lato-normal-italic/lato-normal-italic.woff2") format("woff2"), url("https://assets.gradle.com/lato/fonts/lato-normal-italic/lato-normal-italic.woff") format("woff"); } /* Lato (bold, regular) */ @font-face { font-display: swap; font-family: Lato; font-weight: 500; font-style: normal; src: url("https://assets.gradle.com/lato/fonts/lato-semibold/lato-semibold.woff2") format("woff2"), url("https://assets.gradle.com/lato/fonts/lato-semibold/lato-semibold.woff") format("woff"); } /* Lato (bold, regular) */ @font-face { font-display: swap; font-family: Lato; font-weight: 800; font-style: normal; src: url("https://assets.gradle.com/lato/fonts/lato-heavy/lato-heavy.woff2") format("woff2"), url("https://assets.gradle.com/lato/fonts/lato-heavy/lato-heavy.woff") format("woff"); } /* BEGIN asciidoc.css */ /*! normalize.css v2.1.2 | MIT License | git.io/normalize */ /* ========================================================================== HTML5 display definitions ========================================================================== */ /** Correct `block` display not defined in IE 8/9. */ article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; } /** Correct `inline-block` display not defined in IE 8/9. */ audio, canvas, video { display: inline-block; } /** Prevent modern browsers from displaying `audio` without controls. Remove excess height in iOS 5 devices. */ audio:not([controls]) { display: none; height: 0; } /** Address `[hidden]` styling not present in IE 8/9. Hide the `template` element in IE, Safari, and Firefox < 22. */ [hidden], template { display: none; } script { display: none !important; } /* ========================================================================== Base ========================================================================== */ /** 1. Set default font family to sans-serif. 2. Prevent iOS text size adjust after orientation change, without disabling user zoom. */ html { font-family: sans-serif; /* 1 */ -ms-text-size-adjust: 100%; /* 2 */ -webkit-text-size-adjust: 100%; /* 2 */ } /** Remove default margin. */ body { margin: 0; } /* ========================================================================== Links ========================================================================== */ /** Remove the gray background color from active links in IE 10. */ a { background: transparent; } /** Address `outline` inconsistency between Chrome and other browsers. */ a:focus { outline: thin dotted; } /** Improve readability when focused and also mouse hovered in all browsers. */ a:active, a:hover { outline: 0; } /* ========================================================================== Typography ========================================================================== */ /** Address variable `h1` font-size and margin within `section` and `article` contexts in Firefox 4+, Safari 5, and Chrome. */ h1 { font-size: 2em; margin: 0.67em 0; } /** Address styling not present in IE 8/9, Safari 5, and Chrome. */ abbr[title] { border-bottom: 1px dotted; } /** Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. */ b, strong { font-weight: bold; } /** Address styling not present in Safari 5 and Chrome. */ dfn { font-style: italic; } /** Address differences between Firefox and other browsers. */ hr { -moz-box-sizing: content-box; box-sizing: content-box; height: 0; } /** Address styling not present in IE 8/9. */ mark { background: #ff0; color: var(--black-color); } /** Correct font family set oddly in Safari 5 and Chrome. */ code, kbd, pre, samp { font-family: monospace, serif; font-size: 1em; } /** Improve readability of pre-formatted text in all browsers. */ pre { white-space: pre-wrap; } /** Set consistent quote types. */ q { quotes: "\201C""\201D""\2018""\2019"; } /** Address inconsistent and variable font size in all browsers. */ small { font-size: 80%; } /** Prevent `sub` and `sup` affecting `line-height` in all browsers. */ sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } /* ========================================================================== Embedded content ========================================================================== */ /** Remove border when inside `a` element in IE 8/9. */ img { border: 0; } /** Correct overflow displayed oddly in IE 9. */ svg:not(:root) { overflow: hidden; } /* ========================================================================== Figures ========================================================================== */ /** Address margin not present in IE 8/9 and Safari 5. */ figure { margin: 0; } /* ========================================================================== Forms ========================================================================== */ /** Define consistent border, margin, and padding. */ fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } /** 1. Correct `color` not being inherited in IE 8/9. 2. Remove padding so people aren't caught out if they zero out fieldsets. */ legend { border: 0; /* 1 */ padding: 0; /* 2 */ } /** 1. Correct font family not being inherited in all browsers. 2. Correct font size not being inherited in all browsers. 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. */ button, input, select, textarea { font-family: inherit; /* 1 */ font-size: 100%; /* 2 */ margin: 0; /* 3 */ } /** Address Firefox 4+ setting `line-height` on `input` using `!important` in the UA stylesheet. */ button, input { line-height: normal; } /** Address inconsistent `text-transform` inheritance for `button` and `select`. All other form control elements do not inherit `text-transform` values. Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. Correct `select` style inheritance in Firefox 4+ and Opera. */ button, select { text-transform: none; } /** 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 2. Correct inability to style clickable `input` types in iOS. 3. Improve usability and consistency of cursor style between image-type `input` and others. */ button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ } /** Re-set default cursor for disabled elements. */ button[disabled], html input[disabled] { cursor: default; } /** 1. Address box sizing set to `content-box` in IE 8/9. 2. Remove excess padding in IE 8/9. */ input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ } /** 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome (include `-moz` to future-proof). */ input[type="search"] { -webkit-appearance: textfield; /* 1 */ -moz-box-sizing: content-box; -webkit-box-sizing: content-box; /* 2 */ box-sizing: content-box; } /** Remove inner padding and search cancel button in Safari 5 and Chrome on OS X. */ input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } /** Remove inner padding and border in Firefox 4+. */ button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } /** 1. Remove default vertical scrollbar in IE 8/9. 2. Improve readability and alignment in all browsers. */ textarea { overflow: auto; /* 1 */ vertical-align: top; /* 2 */ } /* ========================================================================== Tables ========================================================================== */ /** Remove most spacing between table cells. */ table { border-collapse: collapse; border-spacing: 0; } meta.foundation-mq-small { font-family: "only screen and (min-width: 768px)"; width: 768px; } meta.foundation-mq-medium { font-family: "only screen and (min-width:1280px)"; width: 1280px; } meta.foundation-mq-large { font-family: "only screen and (min-width:1440px)"; width: 1440px; } *, *:before, *:after { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } html, body { font-size: 100%; } body { background: var(--white-color); color: var(--num-color); padding: 0; margin: 0; font-family: "Noto Serif", "DejaVu Serif", serif; font-weight: normal; font-style: normal; line-height: 1; position: relative; cursor: auto; } a:hover { cursor: pointer; } img, object, embed { max-width: 100%; height: auto; } object, embed { height: 100%; } img { -ms-interpolation-mode: bicubic; } #map_canvas img, #map_canvas embed, #map_canvas object, .map_canvas img, .map_canvas embed, .map_canvas object { max-width: none !important; } .left { float: left !important; } .right { float: right !important; } .text-left { text-align: left !important; } .text-right { text-align: right !important; } .text-center { text-align: center !important; } .text-justify { text-align: justify !important; } .hide { display: none; } .antialiased { -webkit-font-smoothing: antialiased; } img { display: inline-block; vertical-align: middle; } textarea { height: auto; min-height: 50px; } select { width: 100%; } object, svg { display: inline-block; vertical-align: middle; } .center { margin-left: auto; margin-right: auto; } .spread { width: 100%; } p.lead, .paragraph.lead>p, #preamble>.sectionbody>.paragraph:first-of-type p { font-size: 1.21875em; line-height: 1.6; } .subheader, .admonitionblock td.content>.title, .audioblock>.title, .exampleblock>.title, .imageblock>.title, .listingblock>.title, .literalblock>.title, .stemblock>.title, .openblock>.title, .paragraph>.title, .quoteblock>.title, table.tableblock>.title, .verseblock>.title, .videoblock>.title, .dlist>.title, .olist>.title, .ulist>.title, .qlist>.title, .hdlist>.title { line-height: 1.45; color: #7a2518; font-weight: normal; margin-top: 0; margin-bottom: 0.25em; } /* Typography resets */ div, dl, dt, dd, ul, ol, li, h1, h2, h3, #toctitle, .sidebarblock>.content>.title, h4, h5, h6, pre, form, p, blockquote, th, td { margin: 0; padding: 0; direction: ltr; } /* Default Link Styles */ a { color: #2156a5; text-decoration: underline; line-height: inherit; } a:hover, a:focus { color: #1d4b8f; } a img { border: none; } /* Default paragraph styles */ p { font-family: inherit; font-weight: normal; font-size: 1em; line-height: 1.6; margin-bottom: 1.25em; text-rendering: optimizeLegibility; } p aside { font-size: 0.875em; line-height: 1.35; font-style: italic; } /* Default header styles */ h1, h2, h3, #toctitle, .sidebarblock>.content>.title, h4, h5, h6 { font-family: "Open Sans", "DejaVu Sans", sans-serif; font-weight: 300; font-style: normal; color: #ba3925; text-rendering: optimizeLegibility; margin-top: 1em; margin-bottom: 0.5em; line-height: 1.0125em; } h1 small, h2 small, h3 small, #toctitle small, .sidebarblock>.content>.title small, h4 small, h5 small, h6 small { font-size: 60%; color: #e99b8f; line-height: 0; } h1 { font-size: 2.125em; } h2 { font-size: 1.6875em; } h3, #toctitle, .sidebarblock>.content>.title { font-size: 1.375em; } h4 { font-size: 1.125em; } h5 { font-size: 1.125em; } h6 { font-size: 1em; } hr { border: solid #ddddd8; border-width: 1px 0 0; clear: both; margin: 1.25em 0 1.1875em; height: 0; } /* Helpful Typography Defaults */ em, i { font-style: italic; line-height: inherit; } strong, b { font-weight: bold; line-height: inherit; } small { font-size: 60%; line-height: inherit; } code { font-family: "Droid Sans Mono", "DejaVu Sans Mono", monospace; font-weight: normal; color: var(--code-text-color); } a code { color: var(--code-link-color); } /* Lists */ ul, ol, dl { font-size: 1em; line-height: 1.6; margin-bottom: 1.25em; list-style-position: outside; font-family: inherit; } ul, ol { margin-left: 1.5em; } ul.no-bullet, ol.no-bullet { margin-left: 1.5em; } /* Unordered Lists */ ul li ul, ul li ol { margin-left: 1.25em; margin-bottom: 0; font-size: 1em; /* Override nested font-size change */ } ul.square li ul, ul.circle li ul, ul.disc li ul { list-style: inherit; } ul.square { list-style-type: square; } ul.circle { list-style-type: circle; } ul.disc { list-style-type: disc; } ul.no-bullet { list-style: none; } /* Ordered Lists */ ol li ul, ol li ol { margin-left: 1.25em; margin-bottom: 0; } /* Definition Lists */ dl dt { margin-bottom: 0.3125em; font-weight: bold; } dl dd { margin-bottom: 1.25em; } /* Abbreviations */ abbr, acronym { text-transform: uppercase; font-size: 90%; color: var(--num-color); border-bottom: 1px dotted #dddddd; cursor: help; } abbr { text-transform: none; } /* Blockquotes */ blockquote { margin: 0 0 1.25em; padding: 0.5625em 1.25em 0 1.1875em; border-left: 1px solid #dddddd; } blockquote cite { display: block; font-size: 0.9375em; color: rgba(0, 0, 0, 0.6); } blockquote cite:before { content: "\2014 \0020"; } blockquote cite a, blockquote cite a:visited { color: rgba(0, 0, 0, 0.6); } blockquote, blockquote p { line-height: 1.6; color: var(--header-color); } /* Microformats */ .vcard { display: inline-block; margin: 0 0 1.25em 0; border: 1px solid #dddddd; padding: 0.625em 0.75em; } .vcard li { margin: 0; display: block; } .vcard .fn { font-weight: bold; font-size: 0.9375em; } .vevent .summary { font-weight: bold; } .vevent abbr { cursor: auto; text-decoration: none; font-weight: bold; border: none; padding: 0 0.0625em; } @media only screen and (min-width: 768px) { h1, h2, h3, #toctitle, .sidebarblock>.content>.title, h4, h5, h6 { line-height: 1.2; } h1 { font-size: 2.75em; } h2 { font-size: 2.3125em; } h3, #toctitle, .sidebarblock>.content>.title { font-size: 1.6875em; } h4 { font-size: 1.4375em; } } /* Tables */ table { background: var(--white-color); margin-bottom: 1.25em; border: solid 1px #dedede; } table thead, table tfoot { background: var(--table-color); font-weight: bold; } table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td { padding: 0.5em 0.625em 0.625em; font-size: inherit; color: var(--num-color); text-align: left; } table tr th, table tr td { padding: 0.5625em 0.625em; font-size: inherit; color: var(--num-color); } table tr.even, table tr.alt, table tr:nth-of-type(even) { background: var(--nav-color); } table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td { display: table-cell; line-height: 1.6; } body { tab-size: 4; } h1, h2, h3, #toctitle, .sidebarblock>.content>.title, h4, h5, h6 { line-height: 1.2; word-spacing: -0.05em; } h1 strong, h2 strong, h3 strong, #toctitle strong, .sidebarblock>.content>.title strong, h4 strong, h5 strong, h6 strong { font-weight: 400; } .clearfix:before, .clearfix:after, .float-group:before, .float-group:after { content: " "; display: table; } .clearfix:after, .float-group:after { clear: both; } *:not(pre)>code { font-size: 0.9375em; font-style: normal !important; letter-spacing: 0; padding: 0.1em 0.5ex; word-spacing: -0.15em; background-color: var(--code-color); -webkit-border-radius: 4px; border-radius: 4px; line-height: 1.45; text-rendering: optimizeSpeed; word-wrap: break-word; } *:not(pre)>code.nobreak { word-wrap: normal; } *:not(pre)>code.nowrap { white-space: nowrap; } pre, pre>code { line-height: 1.45; color: var(--code-text-color); font-family: "Droid Sans Mono", "DejaVu Sans Mono", "Monospace", monospace; font-weight: normal; text-rendering: optimizeSpeed; } em em { font-style: normal; } strong strong { font-weight: normal; } .keyseq { color: rgba(51, 51, 51, 0.8); } kbd { font-family: "Droid Sans Mono", "DejaVu Sans Mono", monospace; display: inline-block; color: var(--num-color); font-size: 0.65em; line-height: 1.45; background-color: #f7f7f7; border: 1px solid #ccc; -webkit-border-radius: 3px; border-radius: 3px; -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 0.1em white inset; box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 0.1em white inset; margin: 0 0.15em; padding: 0.2em 0.5em; vertical-align: middle; position: relative; top: -0.1em; white-space: nowrap; } .keyseq kbd:first-child { margin-left: 0; } .keyseq kbd:last-child { margin-right: 0; } .menuseq, .menu { color: var(--num-color); } b.button:before, b.button:after { position: relative; top: -1px; font-weight: normal; } b.button:before { content: "["; padding: 0 3px 0 2px; } b.button:after { content: "]"; padding: 0 2px 0 3px; } p a>code:hover { color: var(--code-text-color); } #header, #content, #footnotes, #footer { width: 100%; margin-left: auto; margin-right: auto; margin-top: 0; margin-bottom: 0; max-width: 62.5em; *zoom: 1; position: relative; padding-left: 0.9375em; padding-right: 0.9375em; } #header:before, #header:after, #content:before, #content:after, #footnotes:before, #footnotes:after, #footer:before, #footer:after { content: " "; display: table; } #header:after, #content:after, #footnotes:after, #footer:after { clear: both; } #content { margin-top: 1.25em; } #content:before { content: none; } #header>h1:first-child { color: var(--header-color); margin-top: 2.25rem; margin-bottom: 0; } #header>h1:first-child+#toc { margin-top: 8px; border-top: 1px solid #ddddd8; } #header>h1:only-child, body.toc2 #header>h1:nth-last-child(2) { border-bottom: 1px solid #ddddd8; padding-bottom: 8px; } #header .details { border-bottom: 1px solid #ddddd8; line-height: 1.45; padding-top: 0.25em; padding-bottom: 0.25em; padding-left: 0.25em; color: rgba(0, 0, 0, 0.6); display: -ms-flexbox; display: -webkit-flex; display: flex; -ms-flex-flow: row wrap; -webkit-flex-flow: row wrap; flex-flow: row wrap; } #header .details span:first-child { margin-left: -0.125em; } #header .details span.email a { color: var(--header-color); } #header .details br { display: none; } #header .details br+span:before { content: "\00a0\2013\00a0"; } #header .details br+span.author:before { content: "\00a0\22c5\00a0"; color: var(--header-color); } #header .details br+span#revremark:before { content: "\00a0|\00a0"; } #header #revnumber { text-transform: capitalize; } #header #revnumber:after { content: "\00a0"; } #content>h1:first-child:not([class]) { color: var(--header-color); border-bottom: 1px solid #ddddd8; padding-bottom: 8px; margin-top: 0; padding-top: 1rem; margin-bottom: 1.25rem; } #toc { border-bottom: 1px solid #efefed; padding-bottom: 0.5em; } #toc>ul { margin-left: 0.125em; } #toc ul.sectlevel0>li>a { font-style: italic; } #toc ul.sectlevel0 ul.sectlevel1 { margin: 0.5em 0; } #toc ul { font-family: "Open Sans", "DejaVu Sans", sans-serif; list-style-type: none; } #toc li { line-height: 1.3334; margin-top: 0.3334em; } #toc a { text-decoration: none; } #toc a:active { text-decoration: underline; } #toctitle { color: #7a2518; font-size: 1.2em; } @media only screen and (min-width: 768px) { #toctitle { font-size: 1.375em; } body.toc2 { padding-left: 15em; padding-right: 0; } #toc.toc2 { margin-top: 0 !important; background-color: var(--nav-color); position: fixed; width: 15em; left: 0; top: 0; border-right: 1px solid #efefed; border-top-width: 0 !important; border-bottom-width: 0 !important; z-index: 1000; padding: 1.25em 1em; height: 100%; overflow: auto; } #toc.toc2 #toctitle { margin-top: 0; margin-bottom: 0.8rem; font-size: 1.2em; } #toc.toc2>ul { font-size: 0.9em; margin-bottom: 0; } #toc.toc2 ul ul { margin-left: 0; padding-left: 1em; } #toc.toc2 ul.sectlevel0 ul.sectlevel1 { padding-left: 0; margin-top: 0.5em; margin-bottom: 0.5em; } body.toc2.toc-right { padding-left: 0; padding-right: 15em; } body.toc2.toc-right #toc.toc2 { border-right-width: 0; border-left: 1px solid #efefed; left: auto; right: 0; } } @media only screen and (min-width: 1280px) { body.toc2 { padding-left: 20em; padding-right: 0; } #toc.toc2 { width: 20em; } #toc.toc2 #toctitle { font-size: 1.375em; } #toc.toc2>ul { font-size: 0.95em; } #toc.toc2 ul ul { padding-left: 1.25em; } body.toc2.toc-right { padding-left: 0; padding-right: 20em; } } #content #toc { border-style: solid; border-width: 1px; border-color: #e0e0dc; margin-bottom: 1.25em; padding: 1.25em; background: var(--nav-color); -webkit-border-radius: 4px; border-radius: 4px; } #content #toc> :first-child { margin-top: 0; } #content #toc> :last-child { margin-bottom: 0; } #footer { max-width: 100%; background-color: var(--num-color); padding: 1.25em; } #footer-text { color: rgba(255, 255, 255, 0.8); line-height: 1.44; } .sect1 { padding-bottom: 0.625em; } @media only screen and (min-width: 768px) { .sect1 { padding-bottom: 1.25em; } } .sect1+.sect1 { border-top: 1px solid #efefed; } #content h1>a.anchor, h2>a.anchor, h3>a.anchor, #toctitle>a.anchor, .sidebarblock>.content>.title>a.anchor, h4>a.anchor, h5>a.anchor, h6>a.anchor { position: absolute; z-index: 1001; width: 1.5ex; margin-left: -1.5ex; display: block; text-decoration: none !important; visibility: hidden; text-align: center; font-weight: normal; } #content h1>a.anchor:before, h2>a.anchor:before, h3>a.anchor:before, #toctitle>a.anchor:before, .sidebarblock>.content>.title>a.anchor:before, h4>a.anchor:before, h5>a.anchor:before, h6>a.anchor:before { content: "\00A7"; font-size: 0.85em; display: block; padding-top: 0.1em; } #content h1:hover>a.anchor, #content h1>a.anchor:hover, h2:hover>a.anchor, h2>a.anchor:hover, h3:hover>a.anchor, #toctitle:hover>a.anchor, .sidebarblock>.content>.title:hover>a.anchor, h3>a.anchor:hover, #toctitle>a.anchor:hover, .sidebarblock>.content>.title>a.anchor:hover, h4:hover>a.anchor, h4>a.anchor:hover, h5:hover>a.anchor, h5>a.anchor:hover, h6:hover>a.anchor, h6>a.anchor:hover { visibility: visible; } #content h1>a.link, h2>a.link, h3>a.link, #toctitle>a.link, .sidebarblock>.content>.title>a.link, h4>a.link, h5>a.link, h6>a.link { color: #ba3925; text-decoration: none; } #content h1>a.link:hover, h2>a.link:hover, h3>a.link:hover, #toctitle>a.link:hover, .sidebarblock>.content>.title>a.link:hover, h4>a.link:hover, h5>a.link:hover, h6>a.link:hover { color: #a53221; } .audioblock, .imageblock, .literalblock, .listingblock, .stemblock, .videoblock { margin-bottom: 1.25em; } .admonitionblock td.content>.title, .audioblock>.title, .exampleblock>.title, .imageblock>.title, .listingblock>.title, .literalblock>.title, .stemblock>.title, .openblock>.title, .paragraph>.title, .quoteblock>.title, table.tableblock>.title, .verseblock>.title, .videoblock>.title, .dlist>.title, .olist>.title, .ulist>.title, .qlist>.title, .hdlist>.title { text-rendering: optimizeLegibility; text-align: left; font-family: "Noto Serif", "DejaVu Serif", serif; font-size: 1rem; font-style: italic; } table.tableblock>caption.title { white-space: nowrap; overflow: visible; max-width: 0; } .paragraph.lead>p, #preamble>.sectionbody>.paragraph:first-of-type p { color: var(--header-color); } table.tableblock #preamble>.sectionbody>.paragraph:first-of-type p { font-size: inherit; } /* Custom SVG logos coloring */ path { fill: #02303A; } body.dark-theme { path { fill: white; } } @media (prefers-color-scheme: dark) { path { fill: white; } body.light-theme { path { fill: #02303A; } } } /* Custom scroll bar */ /* Works on Firefox */ .docs-navigation { scrollbar-width: auto; scrollbar-color: #686868 #e7e7e9; } /* Works on Chrome, Edge, and Safari */ .docs-navigation::-webkit-scrollbar { width: 12px; } .docs-navigation::-webkit-scrollbar-track { background: #e7e7e9; } .docs-navigation::-webkit-scrollbar-thumb { background-color: #686868; border-radius: 20px; border: 3px #686868; } body.dark-theme { /* Works on Firefox */ * { scrollbar-width: auto; scrollbar-color: #686868 #242526; } /* Works on Chrome, Edge, and Safari */ *::-webkit-scrollbar { width: auto; } *::-webkit-scrollbar-track { background: #242526; } *::-webkit-scrollbar-thumb { background-color: #686868; border-radius: 20px; border: 3px #686868; } } @media (prefers-color-scheme: dark) { /* Works on Firefox */ * { scrollbar-width: auto; scrollbar-color: #686868 #242526; } /* Works on Chrome, Edge, and Safari */ *::-webkit-scrollbar { width: auto; } *::-webkit-scrollbar-track { background: #242526; } *::-webkit-scrollbar-thumb { background-color: #686868; border-radius: 20px; border: 3px #686868; } body.light-theme { .docs-navigation { scrollbar-width: auto; scrollbar-color: #686868 #e7e7e9; } /* Works on Chrome, Edge, and Safari */ .docs-navigation::-webkit-scrollbar { width: 12px; } .docs-navigation::-webkit-scrollbar-track { background: #e7e7e9; } .docs-navigation::-webkit-scrollbar-thumb { background-color: #686868; border-radius: 20px; border: 3px #686868; } } } /* Custom Admonition Blocks - Icons from https://github.com/primer/octicons */ .admonitionblock td div:last-of-type p { margin-bottom: 0em !important; } .admonitionblock { margin: 1.4rem 0 0 } .admonitionblock i { font-family: inherit; } .admonitionblock i.fa { background: no-repeat 50%/1em 1em; display: inline-block; filter: var(--doc-icon-filter); font-style: normal; height: 1em; -webkit-hyphens: none; hyphens: none; vertical-align: -.125em; width: 1em } .admonitionblock p, .admonitionblock td.content { font-size: 1rem; } .admonitionblock td.content>.title+*, .admonitionblock td.content>:not(.title):first-child { margin-top: 0 } .admonitionblock pre { font-size: calc(15/var(--rem-base)*1rem) } .admonitionblock>table { position: relative; table-layout: fixed; border: none; width: 100% } .admonitionblock td.content { word-wrap: anywhere; background: var(--admonition-background); padding: 1rem 1rem 1rem 1rem; width: 100%; border-radius: 4px; } .admonitionblock td.icon { background: linear-gradient(90deg, rgba(0, 0, 0, .2) 0, rgba(0, 0, 0, .2)) no-repeat 0 /2.075em 100%; border-radius: .5em; font-size: calc(15/var(--rem-base)*1rem); left: 0; line-height: 1; padding: .25em .075em; position: absolute; top: 0; transform: translate(-.5rem, -50%) } .admonitionblock td.icon i { align-items: center; background-position-x: .5em; display: inline-flex; filter: invert(100%); padding-left: 2em; vertical-align: initial; width: auto } .admonitionblock td.icon i::after { content: attr(title); filter: invert(100%); font-style: normal; font-weight: bold; margin: -.05em; padding: 0 .5em; text-transform: uppercase } .admonitionblock.caution td.icon { background-color: var(--caution-color); color: var(--caution-on-color) } .admonitionblock.caution td.icon i { background-image: url(./img/octicons-16.svg#view-flame) } .admonitionblock.important td.icon { background-color: var(--important-color); color: var(--important-on-color) } .admonitionblock.important td.icon i { background-image: url(./img/octicons-16.svg#view-stop) } .admonitionblock.note td.icon { background-color: var(--note-color); color: var(--note-on-color) } .admonitionblock.note td.icon i { background-image: url(./img/octicons-16.svg#view-info) } .admonitionblock.tip td.icon { background-color: var(--tip-color); color: var(--tip-on-color) } .admonitionblock.tip td.icon i { background-image: url(./img/octicons-16.svg#view-light-bulb) } .admonitionblock.warning td.icon { background-color: var(--warning-color); color: var(--warning-on-color) } .admonitionblock.warning td.icon i { background-image: url(./img/octicons-16.svg#view-alert) } /* Custom collapsible block */ details summary { width: 100%; padding: 1rem 0; border-top: 1px solid gray; position: relative; cursor: pointer; list-style: none; } details summary:after { content: "+"; color: var(--black-color); position: absolute; font-size: 1.75rem; line-height: 0; margin-top: 0.3rem; right: 0; font-weight: 400; transform-origin: center; transition: 200ms linear; } details[open] summary:after { transform: rotate(45deg); font-size: 2rem; } details summary { outline: 0; } details p { font-size: 0.95rem; margin: 0 0 1rem; padding-top: 1rem; } .exampleblock>.content { border-style: solid; border-width: 1px; border-color: #e6e6e6; margin-bottom: 1.25em; padding: 1.25em; background: var(--white-color); -webkit-border-radius: 4px; border-radius: 4px; } .exampleblock>.content> :first-child { margin-top: 0; } .exampleblock>.content> :last-child { margin-bottom: 0; } .sidebarblock { border-style: solid; border-width: 1px; border-color: #e0e0dc; margin-bottom: 1.25em; padding: 1.25em; background: var(--nav-color); -webkit-border-radius: 4px; border-radius: 4px; } .sidebarblock> :first-child { margin-top: 0; } .sidebarblock> :last-child { margin-bottom: 0; } .sidebarblock>.content>.title { color: #7a2518; margin-top: 0; text-align: center; } .exampleblock>.content> :last-child> :last-child, .exampleblock>.content .olist>ol>li:last-child> :last-child, .exampleblock>.content .ulist>ul>li:last-child> :last-child, .exampleblock>.content .qlist>ol>li:last-child> :last-child, .sidebarblock>.content> :last-child> :last-child, .sidebarblock>.content .olist>ol>li:last-child> :last-child, .sidebarblock>.content .ulist>ul>li:last-child> :last-child, .sidebarblock>.content .qlist>ol>li:last-child> :last-child { margin-bottom: 0; } .literalblock pre, .listingblock pre:not(.highlight), .listingblock pre[class="highlight"], .listingblock pre[class^="highlight "], .listingblock pre.CodeRay, .listingblock pre.prettyprint { background: var(--code-color); } .sidebarblock .literalblock pre, .sidebarblock .listingblock pre:not(.highlight), .sidebarblock .listingblock pre[class="highlight"], .sidebarblock .listingblock pre[class^="highlight "], .sidebarblock .listingblock pre.CodeRay, .sidebarblock .listingblock pre.prettyprint { background: #f2f1f1; } .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { -webkit-border-radius: 4px; border-radius: 4px; word-wrap: break-word; padding: 1em; font-size: 0.8125em; } .literalblock pre.nowrap, .literalblock pre[class].nowrap, .listingblock pre.nowrap, .listingblock pre[class].nowrap { overflow-x: auto; white-space: pre; word-wrap: normal; } @media only screen and (min-width: 768px) { .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { font-size: 0.90625em; } } @media only screen and (min-width: 1280px) { .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { font-size: 1em; } } .literalblock.output pre { color: var(--code-color); background-color: var(--code-text-color); } .listingblock pre.highlightjs { padding: 0; } .listingblock pre.highlightjs>code { padding: 1em; -webkit-border-radius: 4px; border-radius: 4px; } .listingblock pre.prettyprint { border-width: 0; } .listingblock>.content { position: relative; } .listingblock code[data-lang]:before { display: none; content: attr(data-lang); position: absolute; font-size: 0.75em; top: 0.425rem; right: 0.5rem; line-height: 1; text-transform: uppercase; color: #999; } .listingblock:hover code[data-lang]:before { display: block; } .listingblock.terminal pre .command:before { content: attr(data-prompt); padding-right: 0.5em; color: #999; } .listingblock.terminal pre .command:not([data-prompt]):before { content: "$"; } table.pyhltable { border-collapse: separate; border: 0; margin-bottom: 0; background: none; } table.pyhltable td { vertical-align: top; padding-top: 0; padding-bottom: 0; line-height: 1.45; } table.pyhltable td.code { padding-left: .75em; padding-right: 0; } pre.pygments .lineno, table.pyhltable td:not(.code) { color: #999; padding-left: 0; padding-right: .5em; border-right: 1px solid #ddddd8; } pre.pygments .lineno { display: inline-block; margin-right: .25em; } table.pyhltable .linenodiv { background: none !important; padding-right: 0 !important; } .quoteblock { margin: 0 1em 1.25em 1.5em; display: table; } .quoteblock>.title { margin-left: -1.5em; margin-bottom: 0.75em; } .quoteblock blockquote, .quoteblock blockquote p { color: var(--header-color); font-size: 1.15rem; line-height: 1.75; word-spacing: 0.1em; letter-spacing: 0; font-style: italic; text-align: justify; } .quoteblock blockquote { margin: 0; padding: 0; border: 0; } .quoteblock blockquote:before { content: "\201c"; float: left; font-size: 2.75em; font-weight: bold; line-height: 0.6em; margin-left: -0.6em; color: var(--quoteblock-color); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); } .quoteblock blockquote>.paragraph:last-child p { margin-bottom: 0; } .quoteblock .attribution { margin-top: 0.5em; margin-right: 0.5ex; text-align: right; } .quoteblock .quoteblock { margin-left: 0; margin-right: 0; padding: 0.5em 0; border-left: 3px solid rgba(0, 0, 0, 0.6); } .quoteblock .quoteblock blockquote { padding: 0 0 0 0.75em; } .quoteblock .quoteblock blockquote:before { display: none; } .verseblock { margin: 0 1em 1.25em 1em; } .verseblock pre { font-family: "Open Sans", "DejaVu Sans", sans; font-size: 1.15rem; color: var(--header-color); font-weight: 300; text-rendering: optimizeLegibility; } .verseblock pre strong { font-weight: 400; } .verseblock .attribution { margin-top: 1.25rem; margin-left: 0.5ex; } .quoteblock .attribution, .verseblock .attribution { font-size: 0.9375em; line-height: 1.45; font-style: italic; } .quoteblock .attribution br, .verseblock .attribution br { display: none; } .quoteblock .attribution cite, .verseblock .attribution cite { display: block; letter-spacing: -0.025em; color: rgba(0, 0, 0, 0.6); } .quoteblock.abstract { margin: 0 0 1.25em 0; display: block; } .quoteblock.abstract blockquote, .quoteblock.abstract blockquote p { text-align: left; word-spacing: 0; } .quoteblock.abstract blockquote:before, .quoteblock.abstract blockquote p:first-of-type:before { display: none; } table.tableblock { max-width: 100%; border-collapse: separate; } table.tableblock td>.paragraph:last-child p>p:last-child, table.tableblock th>p:last-child, table.tableblock td>p:last-child { margin-bottom: 0; } table.tableblock, th.tableblock, td.tableblock { border: 0 solid #dedede; } table.grid-all th.tableblock, table.grid-all td.tableblock { border-width: 0 1px 1px 0; } table.grid-all tfoot>tr>th.tableblock, table.grid-all tfoot>tr>td.tableblock { border-width: 1px 1px 0 0; } table.grid-cols th.tableblock, table.grid-cols td.tableblock { border-width: 0 1px 0 0; } table.grid-all *>tr>.tableblock:last-child, table.grid-cols *>tr>.tableblock:last-child { border-right-width: 0; } table.grid-rows th.tableblock, table.grid-rows td.tableblock { border-width: 0 0 1px 0; } table.grid-all tbody>tr:last-child>th.tableblock, table.grid-all tbody>tr:last-child>td.tableblock, table.grid-all thead:last-child>tr>th.tableblock, table.grid-rows tbody>tr:last-child>th.tableblock, table.grid-rows tbody>tr:last-child>td.tableblock, table.grid-rows thead:last-child>tr>th.tableblock { border-bottom-width: 0; } table.grid-rows tfoot>tr>th.tableblock, table.grid-rows tfoot>tr>td.tableblock { border-width: 1px 0 0 0; } table.frame-all { border-width: 1px; } table.frame-sides { border-width: 0 1px; } table.frame-topbot { border-width: 1px 0; } th.halign-left, td.halign-left { text-align: left; } th.halign-right, td.halign-right { text-align: right; } th.halign-center, td.halign-center { text-align: center; } th.valign-top, td.valign-top { vertical-align: top; } th.valign-bottom, td.valign-bottom { vertical-align: bottom; } th.valign-middle, td.valign-middle { vertical-align: middle; } table thead th, table tfoot th { font-weight: bold; } tbody tr th { display: table-cell; line-height: 1.6; background: var(--table-color); } tbody tr th, tbody tr th p, tfoot tr th, tfoot tr th p { color: var(--num-color); font-weight: bold; } p.tableblock>code:only-child { background: none; padding: 0; } p.tableblock { font-size: 1em; } td>div.verse { white-space: pre; } ol { margin-left: 1.75em; } ul li ol { margin-left: 1.5em; } dl dd { margin-left: 1.125em; } dl dd:last-child, dl dd:last-child> :last-child { margin-bottom: 0; } ol>li p, ul>li p, ul dd, ol dd, .olist .olist, .ulist .ulist, .ulist .olist, .olist .ulist { margin-bottom: 0.625em; } ul.unstyled, ol.unnumbered, ul.checklist, ul.none { list-style-type: none; } ul.unstyled, ol.unnumbered, ul.checklist { margin-left: 0.625em; } ul.checklist li>p:first-child>.fa-square-o:first-child, ul.checklist li>p:first-child>.fa-check-square-o:first-child { width: 1em; font-size: 0.85em; } ul.checklist li>p:first-child>input[type="checkbox"]:first-child { width: 1em; position: relative; top: 1px; } ul.inline { margin: 0 auto 0.625em auto; margin-left: -1.375em; margin-right: 0; padding: 0; list-style: none; overflow: hidden; } ul.inline>li { list-style: none; float: left; margin-left: 1.375em; display: block; } ul.inline>li>* { display: block; } .unstyled dl dt { font-weight: normal; font-style: normal; } ol.arabic { list-style-type: decimal; } ol.decimal { list-style-type: decimal-leading-zero; } ol.loweralpha { list-style-type: lower-alpha; } ol.upperalpha { list-style-type: upper-alpha; } ol.lowerroman { list-style-type: lower-roman; } ol.upperroman { list-style-type: upper-roman; } ol.lowergreek { list-style-type: lower-greek; } .hdlist>table, .colist>table { border: 0; background: none; } .hdlist>table>tbody>tr, .colist>table>tbody>tr { background: none; } td.hdlist1, td.hdlist2 { vertical-align: top; padding: 0 0.625em; } td.hdlist1 { font-weight: bold; padding-bottom: 1.25em; } .literalblock+.colist { margin-top: -0.5em; } .colist>table tr>td:first-of-type { padding: 0 0.75em; line-height: 1; } .colist>table tr>td:first-of-type img { max-width: initial; } .colist>table tr>td:last-of-type { padding: 0.25em 0; } .thumb, .th { line-height: 0; display: inline-block; border: solid 4px var(--white-color); -webkit-box-shadow: 0 0 0 1px #dddddd; box-shadow: 0 0 0 1px #dddddd; } .imageblock.left, .imageblock[style*="float: left"] { margin: 0.25em 0.625em 1.25em 0; } .imageblock.right, .imageblock[style*="float: right"] { margin: 0.25em 0 1.25em 0.625em; } .imageblock>.title { margin-bottom: 0; } .imageblock.thumb, .imageblock.th { border-width: 6px; } .imageblock.thumb>.title, .imageblock.th>.title { padding: 0 0.125em; } .image.left, .image.right { margin-top: 0.25em; margin-bottom: 0.25em; display: inline-block; line-height: 0; } .image.left { margin-right: 0.625em; } .image.right { margin-left: 0.625em; } a.image { text-decoration: none; display: inline-block; } a.image object { pointer-events: none; } sup.footnote, sup.footnoteref { font-size: 0.875em; position: static; vertical-align: super; } sup.footnote a, sup.footnoteref a { text-decoration: none; } sup.footnote a:active, sup.footnoteref a:active { text-decoration: underline; } #footnotes { padding-top: 0.75em; padding-bottom: 0.75em; margin-bottom: 0.625em; } #footnotes hr { width: 20%; min-width: 6.25em; margin: -0.25em 0 0.75em 0; border-width: 1px 0 0 0; } #footnotes .footnote { padding: 0 0.375em 0 0.225em; line-height: 1.3334; font-size: 0.875em; margin-left: 1.2em; text-indent: -1.05em; margin-bottom: 0.2em; } #footnotes .footnote a:first-of-type { font-weight: bold; text-decoration: none; } #footnotes .footnote:last-of-type { margin-bottom: 0; } #content #footnotes { margin-top: -0.625em; margin-bottom: 0; padding: 0.75em 0; } .gist .file-data>table { border: 0; background: var(--white-color); width: 100%; margin-bottom: 0; } .gist .file-data>table td.line-data { width: 99%; } div.unbreakable { page-break-inside: avoid; } .big { font-size: larger; } .small { font-size: smaller; } .underline { text-decoration: underline; } .overline { text-decoration: overline; } .line-through { text-decoration: line-through; } .green { color: #006000; } .red { color: #bf0000; } .yellow { color: #bfbf00; } span.icon>.fa { cursor: default; } .conum[data-value] { display: inline-block; color: var(--white-color) !important; background-color: var(--num-color); -webkit-border-radius: 100px; border-radius: 100px; text-align: center; font-size: 0.75em; width: 1.67em; height: 1.67em; line-height: 1.67em; font-family: "Open Sans", "DejaVu Sans", sans-serif; font-style: normal; font-weight: bold; } .conum[data-value] * { color: var(--white-color) !important; } .conum[data-value]+b { display: none; } .conum[data-value]:after { content: attr(data-value); } pre .conum[data-value] { position: relative; top: -0.125em; } b.conum * { color: inherit !important; } .conum:not([data-value]):empty { display: none; } dt, th.tableblock, td.content, div.footnote { text-rendering: optimizeLegibility; } h1, h2, p, td.content, span.alt { letter-spacing: -0.01em; } p strong, td.content strong, div.footnote strong { letter-spacing: -0.005em; } p, blockquote, dt, td.content, span.alt { font-size: 1.0625rem; } p { margin-bottom: 1.25rem; } .sidebarblock p, .sidebarblock dt, .sidebarblock td.content, p.tableblock { font-size: 1em; } .exampleblock>.content { background-color: #fffef7; border-color: #e0e0dc; -webkit-box-shadow: 0 1px 4px #e0e0dc; box-shadow: 0 1px 4px #e0e0dc; } .print-only { display: none !important; } @media print { @page { margin: 1.25cm 0.75cm; } * { -webkit-box-shadow: none !important; box-shadow: none !important; text-shadow: none !important; } a { color: inherit !important; text-decoration: underline !important; } a.bare, a[href^="#"], a[href^="mailto:"] { text-decoration: none !important; } a[href^="http:"]:not(.bare):after, a[href^="https:"]:not(.bare):after { content: "(" attr(href) ")"; display: inline-block; font-size: 0.875em; padding-left: 0.25em; } abbr[title]:after { content: " (" attr(title) ")"; } pre, blockquote, tr, img, object, svg { page-break-inside: avoid; } thead { display: table-header-group; } svg { max-width: 100%; } p, blockquote, dt, td.content { font-size: 1em; orphans: 3; widows: 3; } h2, h3, #toctitle, .sidebarblock>.content>.title, #toctitle, .sidebarblock>.content>.title { page-break-after: avoid; } #toc, .sidebarblock, .exampleblock>.content { background: none !important; } #toc { border-bottom: 1px solid #ddddd8 !important; padding-bottom: 0 !important; } .sect1 { padding-bottom: 0 !important; } .sect1+.sect1 { border: 0 !important; } #header>h1:first-child { margin-top: 1.25rem; } body.book #header { text-align: center; } body.book #header>h1:first-child { border: 0 !important; margin: 2.5em 0 1em 0; } body.book #header .details { border: 0 !important; display: block; padding: 0 !important; } body.book #header .details span:first-child { margin-left: 0 !important; } body.book #header .details br { display: block; } body.book #header .details br+span:before { content: none !important; } body.book #toc { border: 0 !important; text-align: left !important; padding: 0 !important; margin: 0 !important; } body.book #toc, body.book #preamble, body.book h1.sect0, body.book .sect1>h2 { page-break-before: always; } .listingblock code[data-lang]:before { display: block; } #footer { background: none !important; padding: 0 0.9375em; } #footer-text { color: rgba(0, 0, 0, 0.6) !important; font-size: 0.9em; } .hide-on-print { display: none !important; } .print-only { display: block !important; } .hide-for-print { display: none !important; } .show-for-print { display: inherit !important; } } /* END asciidoc.css */ html, body { margin: 0; padding: 0; } html { font-size: 16px; font-weight: 400; line-height: 1.5; } body { color: var(--text-color); background-color: var(--white-color); font-family: "Lato", "Helvetica Neue", Arial, sans-serif; line-height: 1.5; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } /* Links */ a { color: #1DA2BD; text-decoration: none; } a:hover, a:focus { text-decoration: underline; } #content a[href^='../dsl/'], #content a[href^='../kotlin-dsl/'], #content a[href^='../javadoc/'] { font-family: 'Inconsolata', monospace; font-style: normal; border-bottom: 1px dotted rgba(29, 162, 189, 0.5); padding: 0 1px; } #content a[href^='../dsl/']:hover, #content a[href^='../dsl/']:focus, #content a[href^='../kotlin-dsl/']:hover, #content a[href^='../kotlin-dsl/']:focus, #content a[href^='../javadoc/']:hover, #content a[href^='../javadoc/']:focus { text-decoration: none; } /* Copy */ p { font-size: 1rem; } pre, pre>code, code { font-family: 'Inconsolata', monospace; } h1, h2, h3, h4, h5, h6, #toctitle, .sidebarblock>.content>.title { font-family: inherit; font-weight: 500; color: inherit; } h1 { font-size: 2rem; } h2 { font-size: 1.5rem; } h3 { font-size: 1.125rem; } h4 { font-size: 1.0625rem; } h5, h6 { font-size: 1rem; } b, strong { font-weight: 500; } dl { margin: 0 0 1.25rem 1.5rem; } .dlist dt code { color: var(--text-color); font-size: 1em; font-weight: bold; } .dlist p { margin-bottom: 0.625rem; } .sr-only { border: 0; clip: rect(0, 0, 0, 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } /* Layout */ .main-content>.appendix, .main-content>.book, .main-content>.chapter, .main-content>.footer { background-color: var(--white-color); border-radius: 5px; max-width: 45rem; padding: 1.5rem; } @media screen and (max-width: 45em) { .footer { max-width: 100%; } .main-content>.appendix, .main-content>.book, .main-content>.chapter { margin-top: 0; margin-bottom: 0; } } /* Override asciidoc styles */ #header { position: static; } #header, #content { padding: 0; } #header .details { /* TODO: Pretty sure there's a way to avoid Asciidoc generating details */ display: none; } p { color: var(--text-color); } h1, h2, p, p strong, td.content, td.content strong, div.footnote strong, span.alt { letter-spacing: normal; } .subheader, .admonitionblock td.content>.title, .audioblock>.title, .exampleblock>.title, .imageblock>.title, .listingblock>.title, .literalblock>.title, .stemblock>.title, .openblock>.title, .paragraph>.title, .quoteblock>.title, table.tableblock>.title, .verseblock>.title, .videoblock>.title, .dlist>.title, .olist>.title, .ulist>.title, .qlist>.title, .hdlist>.title { color: inherit; font-family: inherit; } .listingblock .title, .listingblock .title code { font-style: normal; font-weight: bold; } .imageblock, .videoblock { padding: 0.25em; } p.lead, .paragraph.lead>p, #preamble>.sectionbody>.paragraph:first-of-type p { font-size: 1.0625rem; } .paragraph.lead>p, #preamble>.sectionbody>.paragraph:first-of-type p { color: inherit; } .sect1 { padding-bottom: 0; } .sect1+.sect1 { border: 0 none; } .verseblock pre { font-family: "Lato", Arial, sans-serif; } td.hdlist1 { padding-bottom: 0.625rem; } td.hdlist2 p { margin-bottom: 0.625rem; } body.book #header>h1 { border: 0; } #header>h1:first-child { margin-top: 0; } #content a.link { color: var(--title-color); } .highlight .com { color: #777; } .listingblock pre.highlightjs>code { overflow-x: auto; } .listingblock pre.highlight { overflow-x: auto; } .listingblock pre.highlight>code { white-space: pre; } .conum[data-value] { font-family: "Lato", Arial, sans-serif; } .colist>table tr>td:first-of-type { padding-top: 0.25em; padding-bottom: 0.25em; line-height: 1.4; vertical-align: baseline; } /* * Samples */ .exampleblock>.content { background-color: inherit; border: 0 none; box-shadow: none; padding: 0; padding-bottom: 0.7rem; margin-bottom: 0; } .exampleblock>.content .title { background-color: var(--code-color); border-top: 1px solid #ccc; font-family: 'Inconsolata', monospace; margin: 0; padding: 1em 1em 0; } .exampleblock>.title>a { text-decoration: none; color: var(--num-color); } .exampleblock .listingblock { margin: 0; } /* * Ensure that blocks of code do not wrap by applying the behavior of `[listing%nowrap]` by default. * * These styles are copied from a CSS ruleset in asciidoctor.css that has the same group of * selectors except that they end with `.nowrap`. */ .openblock .content { background: var(--admonition-background); margin-bottom: 1.25em; padding: 1em 1em 0em 1em; border-radius: 4px; overflow: auto !important; } .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { overflow-x: auto; white-space: pre; word-wrap: normal; } /* * This CSS ruleset solves: https://github.com/gradle/guides/issues/113#issuecomment-314826749. */ .literalblock pre::after, .literalblock pre[class]::after, .listingblock pre::after, .listingblock pre[class]::after { content: ""; } .quoteblock blockquote, .quoteblock blockquote p { text-align: left; text-align: start; } div.screenshot { box-shadow: 0 0 20px 1px rgba(0, 0, 0, 0.2); margin-left: auto; margin-right: auto; width: 90%; } .inset { box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1); padding: 1em; } .image.inline-icon img { vertical-align: sub; } /* TOC */ #header>h1:first-child+#toc { background: none; border: 0 none; margin-top: 0; } #toc, #content #toc { border: 0 none; } #toc>ul { margin-left: 0; font-family: inherit; } #toc>ul>li { line-height: 1.25; margin-top: 0; padding-bottom: 0.5rem; } #toc>ul>li:last-of-type { padding-bottom: 0; } #toc a { font-style: normal; } #toc a:hover, #toc a:focus, #toc a:hover code, #toc a:focus code { color: #1DA2BD; } #toc a:active { text-decoration: none; } /* Site header specific styles */ .hamburger { background-color: transparent; background-image: none; border: none; border-radius: 4px; cursor: pointer; margin-left: auto; padding: 11px 10px; } .hamburger:focus { outline: 0; } .hamburger__bar { display: block; width: 22px; height: 2px; background-color: var(--black-color); border-radius: 1px; } .hamburger__bar+.hamburger__bar { margin-top: 4px; } .site-header { background-color: var(--top-header-color); } /* Override javadoc styles */ .site-header div { font-family: 'Lato', Arial, sans-serif; } .site-header__navigation-header a { align-self: center; border-bottom: 0 none; height: 36px; } .site-header .site-header-version { align-self: center; color: #1da2bd; font-size: 20px; padding-left: 1px; margin-top: 22px; } .site-header__navigation { z-index: 2; display: flex; flex-direction: column; } .site-header__navigation-header { display: flex; flex: 0 0 auto; margin-left: 12px; } .site-header__navigation-collapsible { flex: 1 1 auto; height: 210px; overflow: visible; transition: height 0.3s ease; } .site-header__navigation-items { display: flex; flex-direction: column; flex-wrap: wrap; align-items: flex-start; max-height: 210px; /* This matches the collapsible height above */ margin: 0 20px; padding-top: 12px; padding-left: 0; list-style-type: none; } .site-header__navigation-item { flex: 0 1 auto; font-size: 16px; width: 250px; } .site-header__navigation-item .site-header__navigation-link { position: relative; display: inline-block; cursor: pointer; width: 100%; padding: 5px; line-height: 20px; border: 0 none; color: var(--text-color); text-decoration: none; transition: none; -o-transition: none; -moz-transition: none; -webkit-transition: none; } .site-header__navigation-item .site-header__navigation-link:hover { color: #1DA2BD; } .site-header__navigation-item .site-header__navigation-link.active { font-weight: 500; } /* Navigation submenu styles */ .site-header__navigation-submenu-section { position: relative; } .site-header__navigation-submenu-section .site-header__down-arrow { width: 8px; height: 8px; margin-left: 2px; margin-top: 0; } .site-header__navigation-submenu-section .site-header__navigation-link:hover path { fill: none; } .site-header__navigation-submenu-section .site-header__navigation-submenu .site-header__navigation-submenu-item-link:hover { color: #1DA2BD; } .site-header__navigation-submenu-section .site-header__navigation-submenu { display: none; width: 170px; background-color: var(--menu-burger-color); top: 40px; left: 7px; /* NOTE: This must match the padding of .site-header__navigation-link */ padding: 3px 10px 6px 10px; z-index: 100; } .site-header__navigation-submenu-section .site-header__navigation-submenu .site-header__navigation-submenu-item-link { width: 100%; color: var(--text-color); white-space: nowrap; display: inline-block; padding-top: 3px; border: 0 none; transition: none; -o-transition: none; -moz-transition: none; -webkit-transition: none; } .site-header__navigation-submenu-section .site-header__navigation-submenu .site-header__navigation-submenu-item-link .site-header__navigation-submenu-item-link-text { display: inline-block; font-size: 16px; } .site-header__navigation-submenu-section.open .site-header__navigation-submenu { display: block; } /* Top navigation mobile styles */ @media (max-width: 1023px) { .site-header__navigation-collapsible--collapse { height: 0; overflow-y: hidden; } .site-header__navigation-submenu-section .site-header__navigation-submenu { padding: 0 1rem 0.5rem 1.5rem; display: block; top: 30px !important; left: 0 !important; } .site-header__navigation-item, .site-header__navigation-submenu-section .site-header__navigation-submenu .site-header__navigation-submenu-item-link .site-header__navigation-submenu-item-link-text { font-size: 18px; } .site-header { padding: 5px 12px; } .site-header-version { display: none; } .site-footer__navigation { flex-direction: column; } .site-footer__links { flex-wrap: wrap; } .site-footer__link-group { margin-bottom: 1rem; } } /* Top navigation desktop styles */ @media (min-width: 1024px) { .site-header { -webkit-box-shadow: 0 2px 4px 0 var(--box-shadow-color); -moz-box-shadow: 0 2px 4px 0 var(--box-shadow-color); box-shadow: 0 2px 4px 0 var(--box-shadow-color); z-index: 2; } body.dark-theme { .site-header { -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; z-index: 2; } } @media (prefers-color-scheme: dark) { .site-header { -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; z-index: 2; } body.light-theme { .site-header { -webkit-box-shadow: 0 2px 4px 0 var(--box-shadow-color); -moz-box-shadow: 0 2px 4px 0 var(--box-shadow-color); box-shadow: 0 2px 4px 0 var(--box-shadow-color); z-index: 2; } } } /* Pushes the section headings to just below the top nav bar when a user navigates directly to section anchors. */ #content h2[id], #content h3[id], #content h4[id], #content h5[id] { padding-top: 60px; } #content h2[id] { /* Little extra room above h2s */ margin-top: -1em; } #content h3[id], #content h4[id], #content h5[id] { margin-top: -60px; } .site-header__navigation { flex-direction: row; } .site-header__navigation-button { display: none; } .site-header__navigation-items { flex-direction: row; align-items: center; float: right; width: auto; padding-top: 0; } .site-header__navigation-item { width: auto; } .site-header__navigation-item .site-header__navigation-link { padding: 15px 18px; } .site-header__navigation-item:last-of-type .site-header__navigation-link { padding-right: 0; } .site-header__navigation-link--button { padding: 6px 12px; } .site-header__navigation-collapsible { height: auto; } .site-header__navigation-submenu-section .site-header__navigation-submenu { position: absolute; border: 1px solid #9a9a9a; border-radius: 3px; } .site-header__navigation-submenu-section:hover .site-header__navigation-submenu { display: block; } /* Pushes the section headings to just below the top nav bar when a user navigates directly to section anchors. It doesn't work if you try to apply the padding and margin to the `h` elements directly. */ .chapter a[name], .chapter .anchor { padding-top: 60px; margin-top: -60px; text-decoration: none; border: none; display: inline-block; } } /* Side Navigation styles */ /* Docs Navigation */ .docs-navigation { border-right: 1px solid var(--various-border-color); } .docs-navigation .search-container { display: none; margin-bottom: 1rem; } .docs-navigation a { color: var(--text-color); display: block; font-size: .95rem; position: relative; } .docs-navigation a:focus { outline: none; } .docs-navigation a:hover { color: #35c1e4; text-decoration: none; } .docs-navigation a code { color: var(--text-color); overflow-wrap: break-word; padding: 0; word-break: break-all; } .docs-navigation a.active { color: #06A0CE; outline: 0; border: none; -moz-outline-style: none; } .docs-navigation a.active:hover { color: #35c1e4; text-decoration: underline; } .docs-navigation ul { list-style-type: none; margin: 0; padding: 0; } .docs-navigation li>ul>li a { font-size: 14px; color: #7d7d7d; } .docs-navigation ul:last-of-type { margin-bottom: 0; } .docs-navigation li { margin-top: 0.3334em; line-height: 1.3334; } .docs-navigation li:last-of-type { margin-bottom: 0; } .docs-navigation .nav-dropdown:before { content: '\2023'; font-size: 28px; position: absolute; margin-left: -14px; margin-top: -8px; } .docs-navigation .nav-dropdown.expanded:before { transform: rotate(90deg); } .docs-navigation>ul ul, .docs-navigation>ul ul ul { display: none; height: 0; margin-left: 1rem; } .docs-navigation>ul ul:target, .docs-navigation>ul ul:target ul, .docs-navigation>ul .nav-dropdown.expanded~ul { display: block; height: auto; } .docs-navigation h3 { font-size: .95rem; font-weight: 600; line-height: 1.5; margin: 1.5em 0 0; } .docs-navigation .docs-home-link { position: relative; } @media screen and (min-width: 45rem) { .main-content { display: flex; } } /* User guide navigation appears for desktops */ @media screen and (min-width: 64rem) { .docs-navigation { flex: 0 0 auto; width: 13.75rem; } .main-content>.appendix, .main-content>.book, .main-content>.chapter { flex: 0 0 auto; margin: 0 auto; } } /** * For mobile devices, we show navigation at bottom of page. * * This is the simplest solution to this issue. */ @media not screen and (min-width: 64rem) { /* Repeat the class twice to prioritize our mobile classes! */ .content.content { /* Make the height equal to the real height of content */ overflow: visible; } .main-content { /* Main content show first */ flex-direction: column-reverse; } .docs-navigation.docs-navigation { /* Fill the main container */ width: 100%; /* Don't clip the navigation container */ overflow: visible; } } /* Userguide Meta */ .chapter-meta { float: right; text-align: right; } .chapter-meta .edit-link { color: #999; font-size: 0.9em; padding-right: 3px; } .chapter-meta .edit-link svg { margin-right: 1px; } /* Clever use of RTL to fill in all stars to left of hover position */ .rating { direction: rtl; } .rating>.star { cursor: pointer; display: table-cell; padding: 3px; } .rating>.star:hover>svg>g, .rating>.star:hover~.star>svg>g, .rating>.star.selected>svg>g, .rating>.star.selected~.star>svg>g { fill: #999; } /* Footer styles */ .site-footer {} .site-footer__navigation { display: flex; padding: 30px 12px; max-width: 62.5rem; margin: 0 auto; padding-left: 5rem; } @media not screen and (min-width: 64rem) { .site-footer__navigation { /* same to nav.docs-navigation for mobiles */ padding: 20px 20px 20px 26px; } } .site-footer__links { display: flex; flex: 1 1 auto; } .site-footer__links .site-footer__links-list { list-style-type: none; margin: 0; } .site-footer__links .site-footer__links-list a { color: var(--footer-other-text-color); } .site-footer__link-group { flex: 1 1 auto; flex-basis: 175px; } .site-footer__link-group header { color: var(--text-color); } .site-footer__subscribe-newsletter .newsletter-form__header h5 { color: var(--text-color); margin-top: 0; } .site-footer__subscribe-newsletter p { font-size: 0.875rem; margin: 2px 0 0 2px; opacity: 0.7; } .site-footer__subscribe-newsletter .disclaimer { color: var(--footer-other-text-color); font-size: 0.75rem; opacity: 0.55; } .site-footer__subscribe-newsletter .newsletter-form { padding-top: 6px; display: flex; justify-content: flex-start; } .site-footer__subscribe-newsletter .email, .site-footer__subscribe-newsletter .submit { height: 40px; } .site-footer__subscribe-newsletter .email { line-height: 40px; width: 250px; color: #1DA2BD; font-size: 16px; padding-left: 20px; border-top-left-radius: 4px; border-bottom-left-radius: 4px; border: 1px solid var(--footer-form-color); } .site-footer__subscribe-newsletter .submit { font-family: inherit; font-size: inherit; line-height: inherit; width: 100px; background-color: #1BA8CB; color: #fff; font-weight: 500; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-style: none; cursor: pointer; transition: all .3s ease; } /* Secondary footer (below) */ .site-footer-secondary { background-color: var(--footer-white-color); border-top: 1px solid var(--various-border-color); width: 100%; z-index: 1; } body.dark-theme { .site-footer-secondary { background-color: var(--footer-white-color); border-top: none; width: 100%; z-index: 1; } } @media (prefers-color-scheme: dark) { .site-footer-secondary { background-color: var(--footer-white-color); border-top: none; width: 100%; z-index: 1; } body.light-theme { .site-footer-secondary { background-color: var(--footer-white-color); border-top: 1px solid var(--various-border-color); width: 100%; z-index: 1; } } } .site-footer-secondary__contents { display: flex; align-items: center; justify-content: space-between; max-width: 75rem; margin-left: auto; margin-right: auto; font-size: 0.875rem; padding: 0.5rem 0.75rem; } /* * 1. Value is the largest computed width among 'site-footer__copy' and 'site-footer__links'. */ .site-footer__copy, .site-footer__secondary-links { flex-grow: 0; flex-basis: 280px; /* 1. */ } /* * 1. 'flex-shrink: 1' is applied to the element with the smallest computed width among * 'site-footer__copy' and 'site-footer__links'. */ .site-footer__copy { flex-shrink: 1; /* 1. */ } .site-footer__logo { flex: 0 0 auto; margin-right: 10px; margin-left: 10px; } .site-footer__logo svg { width: 35px; height: 35px; } /* * 1. 'flex-shrink: 0' is applied to the element with the largest computed width among * 'site-footer__copy' and 'site-footer__links'. */ .site-footer__secondary-links { flex-shrink: 0; /* 1 */ text-align: right; white-space: nowrap; } .site-footer-secondary a { color: #999; } .site-footer-secondary__links a:not(:last-child) { padding-right: 10px; } .site-footer-secondary__links a:not(:first-child) { padding-left: 10px; } @media all and (max-width: 29.99rem) { .site-footer__rights, .site-footer-secondary__links { display: none; } .site-footer__logo { order: 1; text-align: left; } .site-footer__copy { order: 2; text-align: right; } } /* Avoid the footer taking up much of the screen on short displays */ @media all and (max-height: 56.25rem) { .site-footer__navigation { margin: 1.5rem auto 0 auto; padding-top: 0; padding-bottom: 0; } } @media screen and (min-width: 84.375rem) { .ui-logos .ui-logo { box-shadow: 0 6px 15px 1px rgba(0, 0, 0, 0.56); } } /* User Manual Home */ .technology-logos, .ui-logos { display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-around; } .technology-logo, .ui-logo { flex: 0 1 auto; } .ui-logo { width: 224px; height: 135px; margin: 12px; } /* Samples download button */ .download { display: flex; } .download a { border-style: solid; border-width: 1px; text-decoration: none; padding: 5px; display: block; width: 10em; margin: 5px; } .download ul { list-style: none; list-style-type: none; } .download li { float: right; } .docs-navigation { width: 18rem; padding: 20px 20px 20px 26px; background: var(--nav-color); overflow-y: scroll; overflow-x: auto; } .layout { display: flex; flex-direction: column; overflow: hidden; height: 100vh; } .main-content { overflow-y: auto; overflow-x: auto; display: flex; } .content { flex: 1 1 auto; overflow: auto; padding-left: 0; padding-right: 0; } .content .chapter { padding: 2rem 2.4rem; } #toc a:active { font-weight: 500; } .site-header { margin-bottom: 1rem; } .site-header__navigation-submenu-item { padding: 2px 0; } .site-footer { background-color: var(--footer-color); padding: 20px 0 40px 0; border-top: 1px solid var(--various-border-color); } #header { margin-left: 0; } #header>h1:first-child { margin-bottom: 40px; } @media screen and (min-width: 64rem) { #header { margin-bottom: 20px; } .site-header { margin-bottom: 0; z-index: 2; } .site-header__navigation-header { margin-top: -7px; } .site-header__navigation-submenu-section:after { content: '\2023'; font-size: 28px; position: absolute; transform: rotate(90deg); margin-right: 10px; top: 5px; right: -7px; } .site-footer__navigation { flex-wrap: wrap; } .site-footer__link-group { flex: 1 1 auto; } .site-footer__links { margin-bottom: 1rem; } } @media screen and (min-width: 75rem) { .content .chapter { box-sizing: content-box; } #content { padding-right: 260px; margin: 0; } /* #toc here must be referenced as #header #toc since we don't want to change single page #toc */ #header #toc { position: fixed; margin: 0 auto; padding-bottom: 0; right: 0; top: 65px; width: 260px; z-index: 1; overflow: auto; border-radius: 0 0 5px 0; max-height: calc(100% - 118px); margin-right: 15px; } #header #toctitle { margin-top: 1.3em; } #header #toc>ul { /* margin-left have to increase if you change border thickness of active toc item */ margin-left: 1px; border-left: 1px solid #666; margin-bottom: 0; padding-right: 10px; padding-bottom: 0.5rem; background-color: var(--white-color); } #header #toc>ul>li, #toc>ul>li:last-of-type { padding: 0.5rem 0; margin: 0; } #header #toc a { padding-left: 10px; font-weight: 400; color: var(--text-color); font-size: .95rem; display: inline-block; } #header #toc a.active { font-weight: 500; border-left: 3px solid #01303a; margin-left: -2px; padding-left: 9px; } .site-footer__navigation { flex-wrap: wrap; margin-left: auto; padding-left: 3rem; } .site-footer__link-group { flex: 0.15 1 auto; } .site-footer__subscribe-newsletter { /* A fix so subscribe disclaimer does not go under long ToC */ max-width: calc(100% - 17rem); } } @media screen and (min-width: 80rem) { .site-footer__subscribe-newsletter { max-height: none; } } @media screen and (min-width: 100rem) { .content .chapter { max-width: 60.5rem; } #header { margin: 0 auto; } #content { padding-right: 0; margin: 0 auto; } #header #toc { right: initial; margin-left: 62.5rem; } .site-footer__navigation { padding-left: 0; } .site-footer__link-group { flex: 0.2 1 auto; } } @media screen and (min-width: 112rem) { .content .chapter { max-width: 62.5rem; margin: 0 auto; padding-right: 3.5rem; position: relative; left: -130px; } #header #toc { right: initial; margin-left: 64.5rem; } .site-footer__link-group { flex: 0.2 1 auto; } } @supports (-moz-appearance:meterbar) and (background-blend-mode:difference, normal) { /* Firefox only */ .site-header__navigation-submenu-section:after { top: 2px; right: -4px; } } /* Added dark mode and other items for outdated Prettify.css */ code[data-lang="text"] .pln, code[data-lang="text"] .str, code[data-lang="text"] .kwd, code[data-lang="text"] .com, code[data-lang="text"] .typ, code[data-lang="text"] .lit, code[data-lang="text"] .pun, code[data-lang="text"] .opn, code[data-lang="text"] .clo, code[data-lang="text"] .tag, code[data-lang="text"] .atn, code[data-lang="text"] .atv, code[data-lang="text"] .dec, code[data-lang="text"] .var, code[data-lang="text"] .fun { color: #000; } body.dark-theme { code[data-lang="text"] .pln, code[data-lang="text"] .str, code[data-lang="text"] .kwd, code[data-lang="text"] .com, code[data-lang="text"] .typ, code[data-lang="text"] .lit, code[data-lang="text"] .pun, code[data-lang="text"] .opn, code[data-lang="text"] .clo, code[data-lang="text"] .tag, code[data-lang="text"] .atn, code[data-lang="text"] .atv, code[data-lang="text"] .dec, code[data-lang="text"] .var, code[data-lang="text"] .fun { color: #fff !important; } .com { color: #909090 !important; } .str, .tag { color: #90a959 !important; } .kwd, .atv { color: #d28445 !important; } .typ { color: #f4bf75 !important; } .lit, .atn { color: #6a9fb5 !important; } .pun, .pln, .opn, .clo { color: #d0d0d0 !important; } .dec, .var { color: #aa759f !important; } li.L1, li.L3, li.L5, li.L7, li.L9 { background: #111 !important; } } @media (prefers-color-scheme: dark) { code[data-lang="text"] .pln, code[data-lang="text"] .str, code[data-lang="text"] .kwd, code[data-lang="text"] .com, code[data-lang="text"] .typ, code[data-lang="text"] .lit, code[data-lang="text"] .pun, code[data-lang="text"] .opn, code[data-lang="text"] .clo, code[data-lang="text"] .tag, code[data-lang="text"] .atn, code[data-lang="text"] .atv, code[data-lang="text"] .dec, code[data-lang="text"] .var, code[data-lang="text"] .fun { color: #fff !important; } .com { color: #909090 !important; } .str, .tag { color: #90a959 !important; } .kwd, .atv { color: #d28445 !important; } .typ { color: #f4bf75 !important; } .lit, .atn { color: #6a9fb5 !important; } .pun, .pln, .opn, .clo { color: #d0d0d0 !important; } .dec, .var { color: #aa759f !important; } li.L1, li.L3, li.L5, li.L7, li.L9 { background: #111 !important; } body.light-theme { code[data-lang="text"] .pln, code[data-lang="text"] .str, code[data-lang="text"] .kwd, code[data-lang="text"] .com, code[data-lang="text"] .typ, code[data-lang="text"] .lit, code[data-lang="text"] .pun, code[data-lang="text"] .opn, code[data-lang="text"] .clo, code[data-lang="text"] .tag, code[data-lang="text"] .atn, code[data-lang="text"] .atv, code[data-lang="text"] .dec, code[data-lang="text"] .var, code[data-lang="text"] .fun { color: #000; } .pln { color: #000 !important;; } .com { color: #800 !important;; } .str, .tag { color: #080 !important;; } .kwd, .atv { color: #008 !important;; } .typ { color: #606 !important;; } .lit, .atn { color: #066 !important;; } .pun, .opn, .clo { color: #660 !important;; } .dec, .var { color: #606 !important;; } li.L1, li.L3, li.L5, li.L7, li.L9 { background: #eee; } } } /* DPE University button*/ /* CSS */ .badge-wrapper { padding-top: 0px; padding-bottom: 20px; a { text-decoration: none; } a:link { text-decoration: none; } a:visited { text-decoration: none; } a:hover { text-decoration: none; } a:active { text-decoration: none; } } .badge { background-color: var(--black-color); height: 24px; border-radius: 12px; border-color: var(--black-color); border-style: solid; border-width: 1px; padding: 5px; a { color: var(--gradle-blue); } } .badge-type { border-radius: 8px; margin-right: 10px; padding-left: 10px; padding-right: 10px; } .badge-text { color: var(--white-color); } .button--blue { color: white; border-color: transparent; &:hover { color: white; } } .button--blue { background: var(--gradle-blue); background: linear-gradient(var(--button-gradient-angle), var(--gradle-blue) 0%, var(--gradle-blue-lite) 100%); &:hover { background: var(--gradle-blue-lite); } } /* Custom Download button*/ /* CSS */ .button-9 { appearance: button; backface-visibility: hidden; background-color: #209BC4; border-radius: 6px; border-width: 0; box-shadow: rgba(50, 50, 93, .1) 0 0 0 1px inset, rgba(50, 50, 93, .1) 0 2px 5px 0, rgba(0, 0, 0, .07) 0 1px 1px 0; box-sizing: border-box; color: #fff; cursor: pointer; font-family: -apple-system, system-ui, "Segoe UI", Roboto, "Helvetica Neue", Ubuntu, sans-serif; font-size: 100%; height: 44px; line-height: 1.15; margin: 12px 12px 12px 0px; outline: none; overflow: hidden; padding: 0 25px; position: relative; text-align: center; text-transform: none; transform: translateZ(0); transition: all .2s, box-shadow .08s ease-in; user-select: none; -webkit-user-select: none; touch-action: manipulation; width: 25%; } .button-9:disabled { cursor: default; } .button-9:focus { box-shadow: rgba(50, 50, 93, .1) 0 0 0 1px inset, rgba(50, 50, 93, .2) 0 6px 15px 0, rgba(0, 0, 0, .1) 0 2px 2px 0, rgba(50, 151, 211, .3) 0 0 0 4px; } .button-9-sect { padding-bottom: 1.25em; } /* Theme toggle switch button */ /* Switch */ .switch { position: relative; display: inline-block; width: 60px; height: 34px; align-self: center; } .switch input { opacity: 0; width: 0; height: 0; } .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; -webkit-transition: 0.4s; transition: 0.4s; } .slider:before { position: absolute; content: ""; height: 26px; width: 26px; left: 4px; bottom: 4px; background-color: white; -webkit-transition: 0.4s; transition: 0.4s; } input:checked+.slider { background-color: #2196f3; } input:checked+.slider:before { -webkit-transform: translateX(26px); -ms-transform: translateX(26px); transform: translateX(26px); } /* Rounded sliders */ .slider { border-radius: 30px; } .slider:before { border-radius: 50%; } </style> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.css"> <meta name="adoc-src-path" content="optimizing-performance/configuration_cache.adoc"> <link crossorigin href="//assets.gradle.com" rel="preconnect"> <link href="https://fonts.googleapis.com/css?family=Inconsolata:400,700" rel="stylesheet"/> <style type="text/css">.multi-language-selector .language-option[data-lang='groovy'], .exampleblock[data-lang=groovy] > .content .title { background-image: url('data:image/svg+xml;base64,PHN2Zw0KICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIg0KICAgd2lkdGg9IjYxNC4wNjA4NSINCiAgIGhlaWdodD0iMzAzLjU5ODYiDQogICB2ZXJzaW9uPSIxLjEiPg0KICA8Zw0KICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMzUuMzk2NTk5LC0zMC41NTgxOTYpIj4NCiAgICA8cGF0aA0KICAgICAgIGQ9Im0gMTU0Ljk3Njg3LDMzMi42OTE2MiBjIDAsLTAuODA1ODcgMTAuODMxMjMsLTE4LjYyMTcyIDI0LjA2OTQsLTM5LjU5MDc2IDEzLjIzODE5LC0yMC45NjkwNSAyMi42NjA1NywtMzguNjIwNjYgMjAuOTM4NjcsLTM5LjIyNTc3IC0xLjcyMTkxLC0wLjYwNTEgLTcuNjc5OTIsMC40ODU2NyAtMTMuMjQwMDIsMi40MjM5MyAtMTYuNzQ1MjQsNS44Mzc0MyAtMjAuMTg2MDUsNC4yOTAxNiAtMzIuNjkzMDgsLTE0LjcwMTU1IC0xMy41MzI2NSwtMjAuNTQ5MDYgLTE0LjAxNzI0LC0yMi42Njk4MyAtNi45NjE1OCwtMzAuNDY2MjQgNC42MDg3NiwtNS4wOTI1NiA0LjMzMTU5LC02LjI3OTY3IC0zLjIzMTksLTEzLjg0MzE2IC00LjU1MDA5LC00LjU1MDEgLTguMjcyODksLTkuNjQzMDkgLTguMjcyODksLTExLjMxNzc2IDAsLTIuODE2NzkgLTMwLjk5MTQ1LC0xNS41ODkzMyAtNzkuOTg5NDk2LC0zMi45NjYxNSAtMTEuMTA5NjYsLTMuOTM5OTcgLTIwLjE5OTM3NSwtNy44MjkyOCAtMjAuMTk5Mzc1LC04LjY0MjkxIDAsLTAuODEzNjYgMjMuNDcwNzM4LC0xLjAwNDc2IDUyLjE1NzE5MSwtMC40MjQ2OSBsIDUyLjE1NzIxLDEuMDU0NjYgNS4wOTEyNywtOC4wMzAwMSBjIDIxLjUxNjQ0LC0zMy45MzU5MiA1NC4wMDk1MiwtNjQuNTg4MjEyIDY4LjQ2NzA1LC02NC41ODgyMTIgMy4wMTA2LDAgOS4xMjE0MSwyLjg2OTE4IDEzLjU3OTUzLDYuMzc1OTQ1IDcuMjU5NDYsNS43MTAyODMgOC4yNTY1LDkuMjQ4NjMyIDkuNTQ5OTQsMzMuODkxOTY3IDAuNzk0MywxNS4xMzM4MiAyLjQyNDIyLDI4LjQ5NjAxIDMuNjIxOTgsMjkuNjkzNzkgMS4xOTc3NSwxLjE5Nzc2IDQuOTk3NjksLTAuMzc0MjUgOC40NDQzMSwtMy40OTMzOSA0LjQwMTQsLTMuOTgzMjMgOS4wMzQ4NCwtNS4wNjMxMyAxNS41Njc2MSwtMy42MjgzIDYuNzY3NTYsMS40ODY0MSAxMC42MjcxLDAuNDY4OTUgMTQuMTY5MzgsLTMuNzM1NDMgNS4zMTM2LC02LjMwNjY2IDMxLjkwNTk2LC00OC45MzcyMDEgMzEuOTA1OTYsLTUxLjE0ODc0NCAwLC0wLjc1MTU4OCA1LjI2NTA5LC05Ljc1NzMwNiAxMS43MDAyMywtMjAuMDEyNzAzIDYuNDM1MDksLTEwLjI1NTM5NyAxMy44NDY2NCwtMjIuMDgwNjMxIDE2LjQ3MDA3LC0yNi4yNzgyODMgNC42NjU1OCwtNy40NjUyMDEgNS4zOTY2LC02LjU4MTczNSAzMy40MjY5Miw0MC4zOTg3MzYgMjguOTQxMjEsNDguNTA3MTY0IDQ0LjUxNzY3LDY2LjQwNjU1NCA1Mi4zMTU3LDYwLjExNzYzNCA1LjQ0MiwtNC4zODg4OSAzNC4wMDA1NSwtNC42Mzc5NCA0MS45ODI0NiwtMC4zNjYxNCA0LjUzNjY1LDIuNDI3OTMgNy40MTkxNiwyLjM5NjEgOS45MjQ4NCwtMC4xMDk2MiA1LjIwMDA3LC01LjIwMDA2IDE3LjY0OTgyLC00LjI5NDM3IDIwLjkwNDEyLDEuNTIwNzggMi41NjM5Myw0LjU4MTQ2IDMuMjE3ODQsNC41ODE0NiA3LjAyMDE0LDAgMi4zMDYwNiwtMi43Nzg2NyA5LjEzNjE5LC01LjA1MjA4IDE1LjE3ODAzLC01LjA1MjA4IDguNTM4ODQsMCAxMS43OTMzNiwxLjc3MzY3IDE0LjYxNDAzLDcuOTY0MzcgbCAzLjYyODgxLDcuOTY0MzYgNTYuMDkyMDMsLTEuOTg1OCBjIDMwLjg1MDYyLC0xLjA5MjIyIDU2LjA5MjA0LC0xLjI4Njg4IDU2LjA5MjA0LC0wLjQzMjU1IDAsMC44NTQyNyAtMjIuOTA2MDgsMTAuMDcxOTUgLTUwLjkwMjQxLDIwLjQ4MzcxIC0yNy45OTYzMiwxMC40MTE3MiAtNTEuMzY3LDE5LjI0MzA1IC01MS45MzQ4NCwxOS42MjUxMiAtMC41Njc4NSwwLjM4MjA3IDAuNjk4NDcsOS40OTI3MyAyLjgxNDA1LDIwLjI0NTg4IDUuNTA0NTcsMjcuOTc5MTcgMS45MTQ1OCw0Ni42MjU4OSAtMTEuOTI2MjksNjEuOTQ2MDQgLTYuMjA5LDYuODcyNjMgLTE2LjI4MzM4LDE0LjE0Mzk1IC0yMi4zODc1MywxNi4xNTg0OSAtNi4xMDQxNSwyLjAxNDU0IC0xMS4wOTg0NCw0LjM4NjE0IC0xMS4wOTg0NCw1LjI3MDE5IDAsMC44ODQwNiA2LjUxNTI1LDExLjY3ODkzIDE0LjQ3ODM1LDIzLjk4ODU5IDcuOTYzMDksMTIuMzA5NjkgMTMuMjgyOCwyMi4zODEyNSAxMS44MjE1MiwyMi4zODEyNSAtMi4zODUwNCwwIC02MC40Njk4NywtMjIuNDQyMzggLTE1MC43ODg5OCwtNTguMjYwNyAtMTcuODA5LC03LjA2MjU4IC0zNC4wMjY4MywtMTIuODQxMDkgLTM2LjAzOTYxLC0xMi44NDEwOSAtMy43NTM2MywwIC0zNi4xNjY5LDEyLjI4NjYyIC0xMzEuMjI0MjMsNDkuNzQyMDYgLTU1LjEyNTI1LDIxLjcyMTAzIC01Ny4yOTIxNywyMi40NzM0OCAtNTcuMjkyMTcsMTkuODk0NTEgeiBtIDExMy4xMTY0OCwtNTEuMjUyNjUgYyAyOS4zMjk0OSwtMTEuMzk1MDYgNTguNDE2NTcsLTIyLjUzMDggNjQuNjM3OTgsLTI0Ljc0NjA3IDEwLjcwMDY4LC0zLjgxMDE5IDE0LjI3OTE4LC0yLjg1NDkzIDY2LjI1Mzk0LDE3LjY4NjM0IDMwLjIxODI1LDExLjk0Mjc0IDYwLjAzMjUyLDIzLjU3NTg3IDY2LjI1MzkyLDI1Ljg1MTM0IDYuMjIxNDEsMi4yNzU1MiAxOS4wNDMxNyw3LjIyMDU5IDI4LjQ5Mjc1LDEwLjk4OTA4IDEwLjc3NTAzLDQuMjk3MDQgMTYuMjgyNTEsNS4zMzE2NiAxNC43NzEwOCwyLjc3NDg0IC0xNi44ODM3LC0yOC41NjExMyAtMTcuMzk5OCwtMjkuMDM3MTYgLTI5LjEwMDgzLC0yNi44NDIwNSAtMTQuMTEzNjEsMi42NDc3MiAtMjIuODc4OTcsLTAuMDY4NSAtMjcuMzgzNjksLTguNDg1NTQgLTIuOTIxMTIsLTUuNDU4MTYgLTIuMDI5NDQsLTguMTI3NTkgNS4yNTQ0MiwtMTUuNzMwMzEgMTAuODQ4ODcsLTExLjMyMzc3IDkuMTQ0NTcsLTIxLjc0MTQ0IC00Ljg0NDI5LC0yOS42MTA5OSAtNS4zNjc2NCwtMy4wMTk1OSAtMTIuMTQwNTksLTEwLjQ4MzcyIC0xNS4wNTEwMiwtMTYuNTg2OTcgbCAtNS4yOTE2OSwtMTEuMDk2ODIgLTExLjM0NTUxLDcuNjk5MjggYyAtMTMuOTQ0NTgsOS40NjMwNCAtMzAuOTUwNjIsOS45NTA2MyAtNDUuMTQ3NjQsMS4yOTQ0OCBsIC0xMC40NjQ4OCwtNi4zODA2NSAtMTIuMTU4NDEsOC4yNjI4NCBjIC0xMi4xNzQ2Miw4LjI3Mzg2IC0yNi4xOTEwOCw5LjM0MjggLTQwLjQzNzUyLDMuMDgzOTIgLTMuOTI0OTgsLTEuNzI0MzkgLTUuNjU1ODMsLTEuMDU0MTUgLTUuNjU1ODMsMi4xOTAwOCAwLDIuNTcxMTggLTMuNzA0MjksNi4zNjI2NSAtOC4yMzE4MSw4LjQyNTUzIC0xMy41NzQyLDYuMTg0NzkgLTI2LjgyMTA3LDQuNTQyNDcgLTM1LjgwMjI5LC00LjQzODc1IGwgLTguMTg5NDQsLTguMTg5NDQgLTYuNjA5ODIsOC40MDMwMyBjIC0zLjYzNTM5LDQuNjIxNjYgLTEyLjA0OTUyLDExLjgzNjk4IC0xOC42OTgwNSwxNi4wMzM5OCAtOC4yMjk2NSw1LjE5NTE4IC0xOC40MTc0MSwxNy44NzE4MiAtMzEuOTE2NCwzOS43MTM2MSAtMTAuOTA1NDksMTcuNjQ1NDYgLTE5LjgyODE2LDMyLjk4MDQyIC0xOS44MjgxNiwzNC4wNzc3NiAwLDEuMDk3MyA4LjM2MjU1LC0xLjUyNzM2IDE4LjU4MzQyLC01LjgzMjU5IDEwLjIyMDg5LC00LjMwNTIxIDQyLjU4MDI4LC0xNy4xNTA4NyA3MS45MDk3NywtMjguNTQ1OTMgeiBtIDI0OC43NjcyOSwtMTIuMjU4NDYgYyAyMi4yNTU1NywtMTIuNTA4MzMgMjcuNDE0MjQsLTM1LjY0ODA4IDE3LjU3Njg1LC03OC44NDMxOCAtOC4yMjE0LC0zNi4wOTk2MSAtMTIuMzI1NSwtNDcuOTEzMjcgLTE3LjE5NDk0LC00OS40OTU5NyAtMy4zNjkwMiwtMS4wOTUwMyAtMy44ODY3NCwwLjEwNjk5IC0xLjk4ODU4LDQuNjE2MzUgMTIuMzE1MTYsMjkuMjU3NzEgMTIuOTk5NjMsNDMuOTQ2NjIgMi4yNDAyMiw0OC4wNzU0MSAtNy4zOTE1MSwyLjgzNjQyIC0xNS4yMzIzOSwtNy43OTA3NSAtMjEuMzk5MDIsLTI5LjAwMzI2IC00Ljc3NTEyLC0xNi40MjU4NyAtOS43Njg2MSwtMjMuODI4MzcgLTEzLjUzMzg3LC0yMC4wNjMxMSAtMC45MDE1NywwLjkwMTYgMC42Nzg0OCw2LjEyMTE3IDMuNTExMjQsMTEuNTk5MTMgMi44MzI3Myw1LjQ3NzkgNi43MTcyMSwyMS45NzQ1NSA4LjYzMjE3LDM2LjY1OTIgMS45MTQ5NywxNC42ODQ2MiA1LjYxMzg1LDMwLjA2MDczIDguMjE5NzMsMzQuMTY5MTIgNi43NzE5OCwxMC42NzY2MSAxNy41NTEyOCw4LjEwMzYgMjQuNTIzODIsLTUuODUzODQgNi44OTM3MywtMTMuNzk5NzUgNy42NzI4NCwtNC42MDc4MSAwLjk2OTMsMTEuNDM2MDIgLTYuOTYwNzQsMTYuNjU5MzcgLTI3LjYzNDQyLDIxLjY1NDgxIC01MS41ODQxOCwxMi40NjQ0MiAtMy44MzMzLC0xLjQ3MDk2IC01LjEzMDM5LDAuMDUxNSAtNS4xMzAzOSw2LjAyMDk3IDAsNC4zOTQzMSAtMi45MzYxOCwxMS43MjI0NSAtNi41MjQ4OCwxNi4yODQ3IGwgLTYuNTI0ODgsOC4yOTUwNiA4Ljk0ODgxLDEuNTExMjMgYyAxNS40NDk0NSwyLjYwOTA5IDM2LjYzMzI4LC0wLjc3NjQyIDQ5LjI1ODYsLTcuODcyMjUgeiBtIC0zMjAuMzY2NSwtMjcuNTM3MzMgYyA0Ni40NTk2NSwtMTMuMzU0NSA1NC43NjE4OCwtMjguOTMwOTMgMzkuMzI4ODUsLTczLjc4NzgyIC0zLjU4MTg1LC0xMC40MTA3OSAtNi41MTI0MywtMjEuNjA5NzEgLTYuNTEyNDMsLTI0Ljg4NjU0IDAsLTExLjE1NzEyIC01LjYxMTUyLC02LjAwMTYxIC05LjgxNzEzLDkuMDE5NDMgLTUuNTYyMDMsMTkuODY1NjEgLTIyLjI5MTE1LDM2Ljc1MDUzIC0zNi4zNDUxNiwzNi42ODM2NCAtMTIuMjA1NDcsLTAuMDU4MiAtMTYuMTgyNTEsLTIuMjgwMDggLTIxLjIyMzc4LC0xMS44NTc2OCAtOS4wNDc0OCwtMTcuMTg4ODIgMC44MTQ2OSwtNDMuNTY3OTcgMjUuOTY5NTQsLTY5LjQ2MjcyIDE4LjQyNDMxLC0xOC45NjYzMDQgMjguNDg4OTMsLTIwLjY4Mzk2NSAyOC40ODg5MywtNC44NjIwNSAwLDI0LjY3MzIgLTE3LjAxMTM0LDYwLjk5NDcgLTI1Ljk3MzA5LDU1LjQ1NjA0IC00LjM4MjA1LC0yLjcwODI2IC0zLjY3MTM4LC0xNC42Njc1MiAxLjQxNzIyLC0yMy44NTAwOSA0LjMyNzM1LC03LjgwODgyIDMuNDQ4MTQsLTE5LjcwNzcxIC0xLjQ1NjIsLTE5LjcwNzcxIC01LjY4Njg5LDAgLTE2Ljk0NTQ3LDIwLjcxMTk2IC0xOC40MzMxOCwzMy45MTA3NCAtMS4zMTUxNSwxMS42NjgyNiAtMC40OTI4NiwxNC41MDU0NSA1LjA2NjUyLDE3LjQ4MDcyIDE5LjQ4MjQ0LDEwLjQyNjcgNDUuMDA1MzEsLTIxLjM5NTQ1IDQ2LjI5MDgzLC01Ny43MTU4NCAwLjY3NTA0LC0xOS4wNzMxODEgLTMuMDE2NzYsLTI0LjQ0MTEwNSAtMTQuNzc5MDYsLTIxLjQ4ODk1MSAtMTcuMTE4NzksNC4yOTY1NDMgLTYzLjIzNDgzLDYzLjc4NjY0MSAtNjMuMjM0ODMsODEuNTczNDYxIDAsMTMuOTEyOCA2Ljk4NzM3LDIzLjY5NzI4IDE5LjI4ODI3LDI3LjAwOTYgMjAuOTE0MTksNS42MzE2MiA0MS4xMzUzOSwtNC4zNzM4OSA1NS40NjMzLC0yNy40NDMzOCBsIDYuMDIxNzUsLTkuNjk1NzEgMC4wMTMyLDEzLjE3MjIzIGMgMC4wMTYyLDE5LjA3MzggLTEyLjQyNjE5LDI5LjEyNzQgLTUxLjY5ODE3LDQxLjc2NzkzIC05Ljc3NjQ5LDMuMTQ2OCAtMTguMTgzMzIsNi4wNTQ0MSAtMTguNjgxOCw2LjQ2MTM3IC0xLjgyMDU2LDEuNDg2MzIgMTYuNjg3MzksMjcuNDc1NzEgMTkuNTY2NDQsMjcuNDc1NzEgMS42MzI2MiwwIDExLjE5MTE1LC0yLjM2MzU5IDIxLjI0MTIzLC01LjI1MjM4IHogTSA0NzYuNDExLDIxNy45NjMwMSBjIDQuNjYzNDcsLTUuMTUzMSA1LjQ4NTUxLC0xMC43NTEwMyA0LjQ4NTc5LC0zMC41NDgyMyAtMS4wODczOSwtMjEuNTMzMzEgLTIuMjc3NDYsLTI1LjMzODUxIC0xMC42NjE0MywtMzQuMDg5NDkgLTE1LjE0ODgyLC0xNS44MTE5NCAtMzMuODYxMjMsLTExLjc3ODI4IC0yMi45MDgxOSw0LjkzODE0IDUuODIyNTYsOC44ODYzNyAxMi45MjU0LDcuMzY4NjQgMTAuNDE2NDEsLTIuMjI1ODEgLTEuOTk4NjQsLTcuNjQyNzYgMS43Mjk5OCwtNy44NDM1OCA4LjIyODM5LC0wLjQ0MzIyIDEyLjE5MzAxLDEzLjg4NTM3IDkuMTE1MzQsMzMuMTI2OTggLTUuMjk4NiwzMy4xMjY5OCAtOS44NjcxNiwwIC0xMi45NDE5NSwtNC4yMzE3NiAtMTkuMzczNTMsLTI2LjY2MzE4IC0yLjkzMDU5LC0xMC4yMjA4NyAtNi40OTk2NCwtMTguNTgzNDMgLTcuOTMxMjcsLTE4LjU4MzQzIC00Ljc2ODk5LDAgLTYuODkyODcsNC4yMDg3OCAtMy45MjcyMSw3Ljc4MjQgMS42MTA4OCwxLjk0MTEgNi4wNzIxOCwxNS44OTEyNyA5LjkxNDA0LDMxLjAwMDQxIDkuNjY5MDMsMzguMDI1OTYgMjMuMTc2NjYsNTEuMDQxNDUgMzcuMDU1NiwzNS43MDU0MyB6IG0gLTE4My43NzEyNSwwLjYwMzk1IGMgMS45ODk3OCwtMS4zMzUgMi44OTk3NSwtNy42NDg5MyAyLjA3ODY1LC0xNC40MjMgLTEuMzI0NDksLTEwLjkyNzAyIC0yLjA2NjYyLC0xMS43MjQxIC03Ljk3MjcsLTguNTYzMjcgLTguNDg3MzMsNC41NDIyNyAtMTcuNjA4MzksLTQuMDYzNTEgLTIwLjcwMjIyLC0xOS41MzI2IC0yLjM0NzA3LC0xMS43MzUzOCAtMy4wNjUyNiwtMTEuNTA0ODkgMTQuMDI1NzYsLTQuNTAxNDkgMi4xNDI4OCwwLjg3ODA4IDcuNTk2NzEsLTIuMTA0MDIgMTIuMTE5NjMsLTYuNjI2OTQgOC4yNzQwOCwtOC4yNzQwOCAxMC45OTgwMiwtMTguMjEyOTkgNC45OTE1NiwtMTguMjEyOTkgLTEuNzc3NTQsMCAtMy4yMzE5LDEuNDU0MzYgLTMuMjMxOSwzLjIzMTkgMCw0LjY0OTIzIC04Ljg0MjMxLDMuOTg5ODIgLTEwLjgzNDY1LC0wLjgwNzk3IC0xLjE2MjI0LC0yLjc5ODc5IC0zLjYzODA3LC0xLjgwNTkxIC04LjA1OTE1LDMuMjMxOSAtMy41MDk4NSwzLjk5OTQ4IC03Ljc5NjczLDcuMjcxNzggLTkuNTI2NDEsNy4yNzE3OCAtMS43Mjk2OSwwIC00LjA2NTYsLTMuMjcyMyAtNS4xOTA4OCwtNy4yNzE3OCAtMS45NzE5NSwtNy4wMDg1NiAtMi4yMTM2NiwtNy4wNTI3NyAtNi42NzczNywtMS4yMjE2OSAtMy45MTA2LDUuMTA4NTQgLTMuOTQ1NTcsNi43MzU4MyAtMC4yMjQ2MSwxMC40NTY2MSAzLjk0MDYyLDMuOTQwNjQgMTEuNDI2MTUsMzAuODcxMDIgMTEuNDI2MTUsNDEuMTA3NDQgMCwxMi43Mjg2NyAxNy41MjkzMywyMi43MzgzMiAyNy43NzgzLDE1Ljg2MjEgeiBtIDU4Ljk0MzA5LC0xNi44NzUzMSBjIDEyLjE0NTY3LC0xMi4xNDU2NyAxNS43NDQ1MiwtMzAuMzUxNDMgOS4xMjk0MywtNDYuMTgzNTcgLTUuMTcwMTYsLTEyLjM3Mzk3IC0xNC4xNDQ2NiwtMTcuMDA1MTYgLTI2Ljc5ODc4LC0xMy44MjkxOCAtOS40MDI2MywyLjM1OTkxIC0yMy43MjMxMSwyOS43NzY1OSAtMjMuNzczODcsNDUuNTE1MzYgLTAuMDc5MiwyNC41MjkwMiAyMy4yNTA1MywzMi42OTAwNSA0MS40NDMyMiwxNC40OTczOSB6IG0gLTIyLjA4MzQxLC0xOS40MzQwNyBjIC0zLjU1NTEsLTMuNTU1MSAtNi40MTc1NSwtMTAuNDYzMjggLTYuMzYxMDIsLTE1LjM1MTUzIDAuMDkzNCwtOC4wNzk2NSAwLjQ5MzE1LC04LjM3MzUgNC4zOTYxOSwtMy4yMzE5IDQuODI0NjQsNi4zNTU2OSAxMy4zOTc5Nyw3LjQ1NjY2IDE2LjY4MjQxLDIuMTQyMzIgMS4xOTQzMiwtMS45MzI0MSAwLjU1Mjk1LC01LjEzMjAyIC0xLjQyNTIsLTcuMTEwMTcgLTUuMDgxMzIsLTUuMDgxMzIgLTQuNDY1NjEsLTExLjk5OTYzIDEuMDY3OTIsLTExLjk5OTYzIDYuMTUxMzcsMCAxNC43MjY3OSwxMi41MTQ3NyAxNC43MjY3OSwyMS40OTE5MSAwLDYuNjA2NzQgLTEzLjQ0NDQsMjAuNTIyOCAtMTkuODI3MjQsMjAuNTIyOCAtMS41Mzc4NSwwIC01LjcwNDc2LC0yLjkwODcyIC05LjI1OTg1LC02LjQ2MzggeiBtIDg2LjM0ODk3LDE4LjU4MzQxIGMgMTMuNDEwNzQsLTE0LjYwMjYzIDEyLjEwMzc2LC0zNy4wOTggLTMuMDk4MjUsLTUzLjMyNjM0IC03Ljg1MDU3LC04LjM4MDYgLTI1LjI5Mjk4LC05LjgyNTUyIC0zMi40MzE4NSwtMi42ODY2NCAtNi41ODc2MSw2LjU4NzYxIC0xMy4xNjQ2NSwzMi40OTMwMyAtMTAuNzIxMjIsNDIuMjI4NDEgNS4yMTEyMiwyMC43NjMxOCAzMi40MDA0MSwyOC44NjY1MiA0Ni4yNTEzMiwxMy43ODQ1NyB6IG0gLTI3LjQ1NTY1LC0yMy42MTQ1MiBjIC01LjMwNjIzLC0zLjcxNjY0IC03LjEyOTQxLC04LjE0OTc0IC02Ljk3ODA2LC0xNi45Njc0NyAwLjE2OTM1LC05Ljg2MDMzIDAuNjg0MywtMTAuNzQ5ODEgMi45NjE0NSwtNS4xMTQxNSAzLjM0NTczLDguMjgwMzIgMTAuMzYyOTIsMTEuNDA3MzQgMTYuNTE4MTQsNy4zNjA5IDMuODE3NTMsLTIuNTA5NjMgMy42MzkyOSwtMy42OTU5NyAtMS4xMDEwNCwtNy4zMjkwNyAtNi4zMjE4OSwtNC44NDUyMyAtNy41NTUwOCwtMTEuNzAxOTEgLTIuMTA0NjUsLTExLjcwMTkxIDQuODczNjQsMCAyMi4zMDQwMiwxOS4wNjY2NiAyMi4zMDQwMiwyNC4zOTc3OCAwLDUuMjAzMTYgLTEyLjM3NzM5LDE0LjM4NTAzIC0xOS4zOTEzOSwxNC4zODUwMyAtMi43NjQwNSwwIC04LjI1NzksLTIuMjY0MDIgLTEyLjIwODQ3LC01LjAzMTExIHogbSAxODkuOTYyOTIsLTE0LjI4ODg3IDMzLjkzNDk1LC0xMi43MzY4MiAtMzYuMzU4ODgsLTAuMTMxMjUgYyAtMzIuNTM5MiwtMC4xMTczNiAtMzYuMzU0MjQsMC40NjMwNyAtMzYuMzE0NzUsNS41MjQ3NSAwLjA0MDEsNS4xMjc1NyAzLjMzMDczLDIwLjI4Mzk1IDQuMzcyNywyMC4xMzk2NyAwLjIzNzIyLC0wLjAzMjkgMTUuNzAxNzcsLTUuNzkxMjQgMzQuMzY1OTgsLTEyLjc5NjQ4IHogbSAtNDQzLjQ5MzQzLC0yLjI3OTMgMy40NjgyNSwtMTAuMjg3NjEgLTMwLjQ2Njg2LC0xLjAyNDA0IGMgLTE2Ljc1Njc0NCwtMC41NjMyMiAtMzAuNDYzNTg0LC0wLjEzMjEzIC0zMC40NTk1OCwwLjk1ODA0IDAuMDA1OCwxLjU5NTEzIDUyLjUwNjU3LDIyLjEwNzA3IDUzLjY2MTc0LDIwLjk2NTUyIDAuMTgwMzUsLTAuMTc4NCAxLjg4ODkyLC00Ljk1MzcyIDMuNzk2NDUsLTEwLjYxMTkxIHogbSAyMDguOTA5MiwtMzEuODg2OTUgYyA3LjE2NjY3LDEuMTQ2IDE1LjI0OTgxLDQuMDc5MjQgMTcuOTYyNTYsNi41MTgzNSA0LjIxMjc5LDMuNzg3ODMgNi4wNDUyOCwzLjUyOTY2IDEyLjU2MjA3LC0xLjc2OTg1IDQuMTk2NDMsLTMuNDEyNSA5LjY1MDI2LC02LjIwNDU3IDEyLjExOTYzLC02LjIwNDU3IDIuNDY5MzksMCA0LjQ4OTgyLC0wLjcxMzA4IDQuNDg5ODIsLTEuNTg0NjcgMCwtMy4wMDkxOCAtNDYuNTM5NTQsLTc5LjIxMjUyIC00OC4zMTQ0NywtNzkuMTA5NjEgLTIuMDg1LDAuMTIwODcyIC00OS43ODYwMyw4MC4wODc5OCAtNTEuMjU0NjQsODUuOTI0MzcgLTAuNjE2MzQsMi40NDk1MiAxLjMxODA0LDMuMzUzNzEgNS4wNzM2NCwyLjM3MTYgMy4zMzgzNiwtMC44NzMgNy45NTQxNiwwLjY4MzI2IDEwLjI1NzI3LDMuNDU4MzYgMy43MzU0LDQuNTAwODcgNS4yNjEwOCw0LjE0MjI5IDE0LjEzMDY4LC0zLjMyMDk3IDguNDI2NDcsLTcuMDkwNDEgMTEuOTMwOCwtOC4wNDg4IDIyLjk3MzQ0LC02LjI4MzAxIHoiDQogICAgICAgc3R5bGU9ImZpbGw6IzMzMzMzMztmaWxsLW9wYWNpdHk6MSIgLz4NCiAgICA8cGF0aA0KICAgICAgIHN0eWxlPSJmaWxsOiM2Mzk4YWE7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiDQogICAgICAgZD0ibSAzMDkuNTQ1NzksMTQwLjg3OTIyIGMgLTAuNTAwNzUsLTAuMjMzMTEgLTEuMTE4MDQsLTAuNzY3OTkgLTIuNTI3OCwtMi4xOTAzNSAtMS45MTE4OCwtMS45Mjg5NyAtMi42NzE4OCwtMi40OTAxMyAtNC4yNjEwOCwtMy4xNDYyOCAtMi4wODc5MSwtMC44NjIwNCAtNC4zNTQ5LC0xLjA5NDIzIC02LjQ4MjMxLC0wLjY2MzkxIC0xLjg5ODA4LDAuMzgzOTMgLTIuNDI0MzUsMC40MjUyNyAtMy4yODI0MiwwLjI1NzggLTEuNDMyMjQsLTAuMjc5NTMgLTEuOTQxNTcsLTEuMTQ5MjkgLTEuNTM1ODMsLTIuNjIyNzIgMC41MDU3MywtMS44MzY1NyA0LjMzNjkzLC04Ljk0MjU1IDEyLjAwODk5LC0yMi4yNzM4NyAxNS41ODM3LC0yNy4wNzg5ODggMzYuNTM2MjQsLTYxLjIyMDk2OCAzOC44NTA1NiwtNjMuMzA2NjYyIDAuMjU2MzEsLTAuMjMwOTk0IDAuMjY1LC0wLjIzMDk5NCAwLjUyMjQ4LDAgMS4zNzc2OCwxLjIzNTk3NiA5LjQ3NjIsMTMuNzI1ODg5IDIwLjU0MDM2LDMxLjY3ODI5OCAxNC45NjY5NiwyNC4yODUwMDQgMjcuNDExMDIsNDUuNjQzNzE0IDI3LjQxMTAyLDQ3LjA0NzcwNCAwLDAuODU3MjkgLTEuMzE2MDgsMS4zNzIxNSAtNC4wODU5MSwxLjU5ODQ1IC0zLjQ0MjI2LDAuMjgxMjQgLTcuNDI0NzgsMi4zMDE4OCAtMTIuODY4ODcsNi41MjkzNiAtMi41ODcxMiwyLjAwODk4IC0zLjY5MTI2LDIuNzU5MzMgLTQuOTUwNjIsMy4zNjQzOCAtMS4wNDkyOCwwLjUwNDExIC0xLjEzNjkyLDAuNTI0MzcgLTIuMjY4NTQsMC41MjQzNyAtMS4xMTM2OCwwIC0xLjIyNjYxLC0wLjAyNDkgLTIuMDk2MjMsLTAuNDYxMjggLTAuNTEyMTMsLTAuMjU3MDIgLTEuNTA0NjksLTAuOTQ5MyAtMi4yNDE1NSwtMS41NjM0MiAtMi4xNzg0OCwtMS44MTU2MSAtNC43OTkxOCwtMy4xMzYxNCAtOS4wNzE5NywtNC41NzEyNCAtNS40Mjk5NywtMS44MjM3NiAtMTEuMjQ0NTQsLTIuOTE4NCAtMTYuMTk1NTEsLTMuMDQ4OTYgLTMuOTA5MzQsLTAuMTAzMDggLTUuNzEyMDEsMC4yNDc5OCAtOC42NzYwMiwxLjY4OTYzIC0yLjQxOTg4LDEuMTc2OTggLTMuNTg2MjEsMi4wMTMxMSAtOS4xMjQ3NSw2LjU0MTQ1IC01LjYzMzc2LDQuNjA2MTcgLTcuNjM0NDIsNS41NjIwNSAtOS42NjQsNC42MTcyNSB6IiAvPg0KICAgIDxwYXRoDQogICAgICAgc3R5bGU9ImZpbGw6IzYzOThhYTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSINCiAgICAgICBkPSJtIDEyOC42Mzc4NiwxNzAuNzM5IGMgLTEwLjE1OSwtMi45NjA3IC00NS41MDMxNzEsLTE2LjkxOTYyIC01MC40NTA5MjQsLTE5LjkyNTIzIC0wLjY4MjkxNiwtMC40MTQ4NCAtMC43MzgwOTgsLTAuNDc3ODcgLTAuNTQzMjA3LC0wLjYyMDM4IDEuMTMxNTYzLC0wLjgyNzQxIDEyLjMzNzk4MywtMS4yMzkwMSAyNS4wMDQwMzEsLTAuOTE4MzYgNS4wNTEzMSwwLjEyNzg4IDM1LjUwMTMyLDEuMTMzMjMgMzUuNTUwOSwxLjE3Mzc3IDAuMDk5NywwLjA4MTUgLTYuNTIxNjQsMTkuMjg4OCAtNy4wMzA1MSwyMC4zOTQzMyAtMC4yMTI1MSwwLjQ2MTcgLTAuNjUwNjMsMC40NDM2NyAtMi41MzAyOSwtMC4xMDQxMyB6IiAvPg0KICAgIDxwYXRoDQogICAgICAgc3R5bGU9ImZpbGw6IzYzOThhYTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSINCiAgICAgICBkPSJtIDU0My42OTQxMywxNzUuMzEwNTUgYyAtMS4zODYyLC0yLjcxMjg3IC0zLjg4NzY5LC0xNC45NzA2NyAtMy45NjA3OCwtMTkuNDA4NTggLTAuMDI4NCwtMS43MjQyNCAwLjE4NjY3LC0yLjQyNzQxIDAuOTYwNiwtMy4xNDA4IDEuNzczNzgsLTEuNjM1MDEgNS4zNzkzOCwtMi4yMjMyIDE1LjI5MDE3LC0yLjQ5NDM0IDUuMDAzNDcsLTAuMTM2ODkgMjguMTAxOSwtMC4xNTc5NiA0NC40MTEyNywtMC4wNDA1IGwgMTEuNDI4NTksMC4wODIzIC0yNy41MTc3LDEwLjMxNDI3IGMgLTI1LjA5MjkyLDkuNDA1NCAtMzkuNjg0NzMsMTQuODQxMjYgLTQwLjIyNDkxLDE0Ljk4NDkgLTAuMTM1MTcsMC4wMzYgLTAuMjY5ODEsLTAuMDY3NCAtMC4zODcyNCwtMC4yOTcyMyBsIDAsMCB6IiAvPg0KICAgIDxwYXRoDQogICAgICAgc3R5bGU9ImZpbGw6IzYzOThhYTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSINCiAgICAgICBkPSJtIDE3OC4wODgzNSwzMTUuNTE0MzMgYyAwLC0wLjc3MDc4IDMuMjY4OSwtNi41MzMxIDExLjU1Mzk1LC0yMC4zNjY5OCAxOS44NTExOCwtMzMuMTQ2MzEgMjguOTI3MTUsLTQ1LjE2MDEyIDQwLjA3MjkyLC01My4wNDQyOSA5Ljk3MTI3LC03LjA1MzM3IDE1LjMwMjc5LC0xMS45Mjk0IDIxLjI0NTM5LC0xOS40MzAzMyBsIDMuNjY0MDksLTQuNjI0OTIgNC45Mzk5LDQuODU2NTIgYyA4LjY0Nzg0LDguNTAxODkgMTMuODYxMywxMC45NjY2OCAyMy4yOTcxNywxMS4wMTQzMiAxMS41NDA1OCwwLjA1ODMgMjQuMTc4NDksLTYuMzg4MSAyNC4xNzg0OSwtMTIuMzMyOTkgMCwtMS4zMzk4IDAuOTU0MDQsLTIuNzIyMzYgMS44Nzg1NywtMi43MjIzNiAwLjM5MTMyLDAgMi42MTY4NiwwLjY4MzY1IDQuOTQ1NjUsMS41MTkyMyA2LjA2OTkyLDIuMTc3OSA5LjYzMDI4LDIuOTY1NDggMTQuNjA5MTIsMy4yMzE2NSA0LjkzOTk4LDAuMjY0MDkgOC41NDg0OCwtMC4xNzA0IDEzLjM1ODE3LC0xLjYwODQxIDQuNzI1NjEsLTEuNDEyODggNy40NDcwOSwtMi44NTkwNSAxNS41OTgzMywtOC4yODg4MiA0LjEwNzYsLTIuNzM2MTggNy41OTk1NCwtNC45NzQ4NyA3Ljc1OTg4LC00Ljk3NDg3IDAuMTYwMzMsMCAzLjEyNTM0LDEuNzE4OTYgNi41ODg5LDMuODE5OTEgMy40NjM1NiwyLjEwMDk1IDcuNjI1NDIsNC4zODEzMyA5LjI0ODU5LDUuMDY3NTEgMTEuMDY4NjIsNC42NzkxOCAyMy4wMjQ0NSw0LjM3MzkgMzMuOTcxMjYsLTAuODY3NDEgMS45NTc5NSwtMC45Mzc0NyA2LjU2NzkzLC0zLjczMDc0IDEwLjI0NDQsLTYuMjA3MjcgMy42NzY0OCwtMi40NzY1MyA2LjczNjEzLC00LjQ0NTc2IDYuNzk5MjMsLTQuMzc2MDYgMC4wNjMxLDAuMDY5NyAxLjQ1NjA1LDIuOTIwOCAzLjA5NTQ0LDYuMzM1NzggMS42Mzk0LDMuNDE0OTggMy42OTg5NSw3LjI2NjcgNC41NzY3OSw4LjU1OTM3IDMuODAwMDEsNS41OTU3OCA4LjE3NTg4LDkuNzUxNDEgMTQuMDgzNDMsMTMuMzc0NjQgNi4wODEyMiwzLjcyOTc1IDkuODMyMDYsOC42ODY4IDEwLjIxODgyLDEzLjUwNTA3IDAuMzgzNjcsNC43Nzk2OCAtMS43MDMyOSw5LjAxOTM5IC03LjcyMTg5LDE1LjY4NzIxIC00LjY5NzQ3LDUuMjA0MTkgLTUuODczMyw3LjIzNTQgLTUuODYxMTMsMTAuMTI0OTEgMC4wMTIyLDIuODkyNjIgMS43MDEwMyw2LjIyODU0IDQuNjY5NjQsOS4yMjM3OSA0LjgwNTMzLDQuODQ4NDQgMTIuMzQ2MDUsNi4yODc3NyAyMy40MzUzNCw0LjQ3MzE5IDIuNDc0MDgsLTAuNDA0ODQgNS41OTQ3OSwtMC43Mzc1NyA2LjkzNDkxLC0wLjczOTM4IDQuMjg1NTEsLTAuMDA2IDYuOTAzMTksMi4wOTU5NSAxMS44OTI1MSw5LjU0ODU5IDMuNDQzNTEsNS4xNDM2NSAxMC45NzM5NiwxNy43NTUwOCAxMC45NzM5NiwxOC4zNzgzNiAwLDAuMzI0MDcgLTAuMzUxNDUsMC41NTIwOCAtMC44NTA5NywwLjU1MjA4IC0xLjA1MTA5LDAgLTUuNjIzMTQsLTEuMjQzOCAtOC45MzIxNCwtMi40Mjk5NCAtNC4zNDg1LC0xLjU1ODc1IC01Ny4yNzIzOCwtMjIuMDA1MjYgLTc0Ljc0Nzg2LC0yOC44Nzc5NSAtNDYuNjYzNSwtMTguMzUxNjIgLTYyLjAyODA1LC0yNC4yMTI0NiAtNjkuNTM2NTcsLTI2LjUyNDg5IC04LjExNDE3LC0yLjQ5ODk1IC0xMy42NDAyOSwtMy4wODUyMiAtMTguMTI2NjEsLTEuOTIzMDYgLTUuNTc5OTYsMS40NDU0NiAtODEuNzIwNjcsMzAuODY4ODggLTEyNi4xMzI0NSw0OC43NDE5NSAtMjEuOTMxMTksOC44MjYgLTI0LjY3NzQ5LDkuODc1NzcgLTI4Ljc4MTE2LDExLjAwMTU5IC0zLjI2MzAzLDAuODk1MjEgLTMuMTQ0MDcsMC44ODI5NCAtMy4xNDQwNywwLjMyNDI2IHoiIC8+DQogIDwvZz4NCjwvc3ZnPg=='); background-position: 16px 80%; background-repeat: no-repeat; background-size: 20px 12px; padding-left: 2.5em; } .multi-language-selector .language-option[data-lang='kotlin'], .exampleblock[data-lang=kotlin] > .content .title { background-image: url("data:image/svg+xml;utf8,<svg viewBox='0 0 8 8' xmlns='http://www.w3.org/2000/svg'><linearGradient id='g' gradientUnits='userSpaceOnUse' x1='8' y1='0' x2='0' y2='8'><stop offset='0' stop-color='%23e44857'/><stop offset='.4689' stop-color='%23c711e1'/><stop offset='1' stop-color='%237f52ff'/></linearGradient><polygon fill='url(%23g)' points='8 8 0 8 0 0 8 0 4 4'/></svg>"); background-position: 17px 80%; background-repeat: no-repeat; background-size: 11px 11px; padding-left: 2.3em; } .multi-language-selector { display: block; } .multi-language-selector .language-option[data-lang='groovy'] { background-position: 20px center; padding-left: 32px; } .multi-language-selector .language-option[data-lang='kotlin'] { background-position: 30px center; padding-left: 27px; } .multi-language-selector .language-option { background-color: white; border: 1px solid #f7f7f8; border-radius: 4px 4px 0 0; cursor: pointer; display: inline-block; font-weight: normal; font-family: 'Lato', Arial, sans-serif; margin: 0; padding: 4px 20px; min-width: 130px; max-width: 320px; text-align: center; filter: grayscale(1); -webkit-filter: grayscale(1); opacity: 0.7; } .multi-language-selector .language-option.selected { background-color: #f7f7f8; color: #02303a; filter: none; -webkit-filter: none; opacity: 1; } .multi-language-text.hidden, .multi-language-selector ~ .multi-language-sample.hidden { display: none; } .multi-language-sample { border-radius: 0 0 4px 4px; } body.dark-theme { .multi-language-selector .language-option { background-color: #1f1f1f; border: 1px solid #1e1e22; } .multi-language-selector .language-option.selected { background-color: #2c2c2c; color: white; } } @media (prefers-color-scheme: dark) { .multi-language-selector .language-option { background-color: #1f1f1f; border: 1px solid #1e1e22; } .multi-language-selector .language-option.selected { background-color: #2c2c2c; color: white; } body.light-theme { .multi-language-selector .language-option { background-color: #white; border: 1px solid #f7f7f8; } .multi-language-selector .language-option.selected { background-color: #f7f7f8; color: #02303a; } } } </style><script type="text/javascript">function postProcessCodeBlocks() { // Assumptions: // 1) All siblings that are marked with class="multi-language-sample" should be grouped // 2) Only one language can be selected per domain (to allow selection to persist across all docs pages) // 3) There is exactly 1 small set of languages to choose from. This does not allow for multiple language preferences. For example, users cannot prefer both Kotlin and ZSH. // 4) Only 1 sample of each language can exist in the same collection. var GRADLE_DSLs = ["kotlin", "groovy"]; var preferredBuildScriptLanguage = initPreferredBuildScriptLanguage(); // Ensure preferred DSL is valid, defaulting to Kotlin DSL function initPreferredBuildScriptLanguage() { var lang = window.localStorage.getItem("preferred-gradle-dsl"); if (GRADLE_DSLs.indexOf(lang) === -1) { window.localStorage.setItem("preferred-gradle-dsl", "kotlin"); lang = "kotlin"; } return lang; } function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); } function processSampleEl(sampleEl, prefLangId) { var codeEl = sampleEl.querySelector("code[data-lang]"); if (codeEl != null) { sampleEl.setAttribute("data-lang", codeEl.getAttribute("data-lang")); if (codeEl.getAttribute("data-lang") !== prefLangId) { sampleEl.classList.add("hidden"); } else { sampleEl.classList.remove("hidden"); } } } function switchSampleLanguage(languageId) { var multiLanguageSampleElements = [].slice.call( document.querySelectorAll(".multi-language-sample") ); // Array of Arrays, each top-level array representing a single collection of samples var multiLanguageSets = []; for (var i = 0; i < multiLanguageSampleElements.length; i++) { var currentCollection = [multiLanguageSampleElements[i]]; var currentSampleElement = multiLanguageSampleElements[i]; processSampleEl(currentSampleElement, languageId); while ( currentSampleElement.nextElementSibling != null && currentSampleElement.nextElementSibling.classList.contains( "multi-language-sample" ) ) { currentCollection.push(currentSampleElement.nextElementSibling); currentSampleElement = currentSampleElement.nextElementSibling; processSampleEl(currentSampleElement, languageId); i++; } multiLanguageSets.push(currentCollection); } multiLanguageSets.forEach(function (sampleCollection) { // Create selector element if not existing if ( sampleCollection.length > 1 && (sampleCollection[0].previousElementSibling == null || !sampleCollection[0].previousElementSibling.classList.contains( "multi-language-selector" )) ) { var languageSelectorFragment = document.createDocumentFragment(); var multiLanguageSelectorElement = document.createElement("div"); multiLanguageSelectorElement.classList.add("multi-language-selector"); languageSelectorFragment.appendChild(multiLanguageSelectorElement); sampleCollection.forEach(function (sampleEl) { var optionEl = document.createElement("code"); var sampleLanguage = sampleEl.getAttribute("data-lang"); optionEl.setAttribute("data-lang", sampleLanguage); optionEl.setAttribute("role", "button"); optionEl.classList.add("language-option"); optionEl.innerText = capitalizeFirstLetter(sampleLanguage); optionEl.addEventListener( "click", function updatePreferredLanguage(evt) { var preferredLanguageId = optionEl.getAttribute("data-lang"); window.localStorage.setItem( "preferred-gradle-dsl", preferredLanguageId ); // Record how far down the page the clicked element is before switching all samples var beforeOffset = evt.target.offsetTop; switchSampleLanguage(preferredLanguageId); // Scroll the window to account for content height differences between different sample languages window.scrollBy(0, evt.target.offsetTop - beforeOffset); } ); multiLanguageSelectorElement.appendChild(optionEl); }); sampleCollection[0].parentNode.insertBefore( languageSelectorFragment, sampleCollection[0] ); } }); [].slice .call( document.querySelectorAll(".multi-language-selector .language-option") ) .forEach(function (optionEl) { if (optionEl.getAttribute("data-lang") === languageId) { optionEl.classList.add("selected"); } else { optionEl.classList.remove("selected"); } }); [].slice .call(document.querySelectorAll(".multi-language-text")) .forEach(function (el) { if (!el.classList.contains("lang-" + languageId)) { el.classList.add("hidden"); } else { el.classList.remove("hidden"); } }); } switchSampleLanguage(preferredBuildScriptLanguage); } document.addEventListener("DOMContentLoaded", function () { postProcessCodeBlocks(); }); </script> <!-- Prefetch header and footer if on Gradle 4.4 and lower only --> <!-- Load build-tool.css if on Gradle<=4.4 or release notes --> <!-- Load build-tool.css if on Gradle<8.0 and javadoc --> <!-- Load build-tool-modern.css if on Gradle>=8.0 and javadoc --> <!-- Load decorate.css if on Gradle<=4.3 for user manual, Gradle<=4.4 for DSL Reference, or Gradle<=4.5 for Javadoc --> <style type="text/css"> cloudflare-app[app="cookiless"] cookiless-div { font-family: "Lato","Helvetica Neue",Arial,sans-serif; font-weight: 300; background-color: #02303A !important; opacity: 1 !important; } cloudflare-app[app="cookiless"] cookiless-div .iAccept { font-weight: 400; } @media (max-width: 1023px) { .notification { display: none; } } @media (min-width: 1024px) { cloudflare-app[app="cookiless"] { position: fixed !important; width: 400px !important; left: auto !important; right: 0 !important; bottom: 0 !important; } /* Overrides for banner */ .notification { height: 44px; line-height: 44px; vertical-align: middle; background-color: #02303A; text-align: center; color: white; z-index: 1; } .notification a { color: white; text-decoration: underline; } .notification ~ .main-content #header #toc, .notification ~ .main-content .toc { top: 98px; } } </style> <!-- Load common JS for all Gradle versions --> <script src="/build-tool.js" type="text/javascript" defer></script> <!-- Google Tag Manager --> <script> (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-WRTQKGT'); </script> <!-- End Google Tag Manager --> <!-- Inject new header and footer for Gradle<=4.4 or release notes--> <script type="text/javascript" defer> window.siteDecorateVersion = "8.11.1"; </script> <!-- Load DocSearch assets if on current manual, dsl, or samples --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@algolia/autocomplete-theme-classic"> <script src="https://cdn.jsdelivr.net/npm/algoliasearch@4.23.3/dist/algoliasearch-lite.umd.js"></script> <script src="https://cdn.jsdelivr.net/npm/instantsearch.js@4.69.0/dist/instantsearch.production.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/@algolia/autocomplete-js"></script> <script src="/build-tool-search.js" type="text/javascript" defer></script> <link rel="stylesheet" href="/build-tool-search.css"> <!--Load ratings css and js if on current user manual --> <link rel="stylesheet" href="/build-tool-rating.css"> <script src="/build-tool-rating.js" type="text/javascript" defer></script> <!--Load banner css, html and js if on current user manual --> <link rel="stylesheet" href="/build-tool-banner.css"> <script src="/build-tool-banner.js" type="text/javascript" defer></script> <!-- Canonical Link and Structured Data for SEO --> <link rel="canonical" href="https://docs.gradle.org/current/userguide/configuration_cache.html" /> <script type="application/ld+json"> { "@context": "http://schema.org", "@type": "WebPage", "name": "Gradle Documentation", "url": "https://docs.gradle.org/current/userguide/configuration_cache.html" } </script> <meta name="google-site-verification" content="kCnBfMu0lbnMpfg3t1-ZgJHbSOSYRSquWsxQ4HgqLkA" /></head> <body id="config_cache" class="book"><!-- Google Tag Manager (noscript) --> <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-WRTQKGT" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> <!-- End Google Tag Manager (noscript) --> <div class="layout"> <header class="site-layout__header site-header" itemscope="itemscope" itemtype="https://schema.org/WPHeader"> <nav class="site-header__navigation" itemscope="itemscope" itemtype="https://schema.org/SiteNavigationElement"> <div class="site-header__navigation-header"> <a target="_top" class="logo" href="https://docs.gradle.org" title="Gradle Docs"> <svg width="139px" height="43px" viewBox="0 0 278 86" version="1.1" xmlns="http://www.w3.org/2000/svg"> <title>Gradle</title> <path class="cls-1" d="M155,56.32V70.27a18.32,18.32,0,0,1-5.59,2.83,21.82,21.82,0,0,1-6.36.89,21.08,21.08,0,0,1-7.64-1.31A17.12,17.12,0,0,1,129.59,69a16.14,16.14,0,0,1-3.73-5.58,18.78,18.78,0,0,1-1.31-7.08,19.58,19.58,0,0,1,1.26-7.14A15.68,15.68,0,0,1,135,40a20.39,20.39,0,0,1,7.45-1.29,22,22,0,0,1,3.92.33,20.43,20.43,0,0,1,3.39.92,15.16,15.16,0,0,1,2.85,1.42A17.3,17.3,0,0,1,155,43.25l-1.84,2.91a1.72,1.72,0,0,1-1.12.84,2,2,0,0,1-1.5-.34L149,45.75a10.49,10.49,0,0,0-1.75-.79,14.33,14.33,0,0,0-2.17-.54,15.29,15.29,0,0,0-2.78-.22,11.91,11.91,0,0,0-4.61.86,9.66,9.66,0,0,0-3.52,2.46,10.9,10.9,0,0,0-2.24,3.84,14.88,14.88,0,0,0-.79,5,15.23,15.23,0,0,0,.85,5.28,11.06,11.06,0,0,0,2.38,3.94A10.15,10.15,0,0,0,138.05,68a14.28,14.28,0,0,0,8.25.44,17.1,17.1,0,0,0,2.94-1.09V61.14h-4.35a1.3,1.3,0,0,1-1-.35,1.15,1.15,0,0,1-.35-.85V56.32Zm10.47-2.93a10.53,10.53,0,0,1,2.72-3.45,5.77,5.77,0,0,1,3.72-1.25,4.5,4.5,0,0,1,2.72.74l-.38,4.41a1.18,1.18,0,0,1-.34.61,1,1,0,0,1-.61.18,6.76,6.76,0,0,1-1.06-.12,8.22,8.22,0,0,0-1.38-.12,5,5,0,0,0-1.74.28,4.37,4.37,0,0,0-1.37.83,5.55,5.55,0,0,0-1.07,1.3,12.26,12.26,0,0,0-.87,1.74V73.61H160V49.14h3.45a1.94,1.94,0,0,1,1.27.32,1.9,1.9,0,0,1,.48,1.16Zm11.36-.84A14.49,14.49,0,0,1,187,48.69a9.92,9.92,0,0,1,3.84.7,8.06,8.06,0,0,1,2.86,2,8.38,8.38,0,0,1,1.78,3,11.64,11.64,0,0,1,.61,3.82V73.61h-2.68a2.64,2.64,0,0,1-1.28-.25,1.72,1.72,0,0,1-.72-1l-.52-1.77a20.25,20.25,0,0,1-1.82,1.47,10.86,10.86,0,0,1-1.83,1.06,10.36,10.36,0,0,1-2,.66,12,12,0,0,1-2.4.22,9.64,9.64,0,0,1-2.86-.41,6.28,6.28,0,0,1-2.27-1.26,5.6,5.6,0,0,1-1.48-2.07,7.38,7.38,0,0,1-.52-2.89,5.7,5.7,0,0,1,.31-1.85,5.3,5.3,0,0,1,1-1.75,8.25,8.25,0,0,1,1.83-1.57,11.17,11.17,0,0,1,2.75-1.29,23.28,23.28,0,0,1,3.81-.9,36.77,36.77,0,0,1,5-.41V58.16a5.35,5.35,0,0,0-1.05-3.64,3.83,3.83,0,0,0-3-1.18,7.3,7.3,0,0,0-2.38.33,9.39,9.39,0,0,0-1.65.75l-1.3.75a2.52,2.52,0,0,1-1.3.34,1.7,1.7,0,0,1-1.05-.32,2.61,2.61,0,0,1-.69-.76Zm13.5,10.61a31.66,31.66,0,0,0-4.3.45,11,11,0,0,0-2.79.82,3.57,3.57,0,0,0-1.5,1.17,2.89,2.89,0,0,0,.47,3.67,3.93,3.93,0,0,0,2.39.67,7,7,0,0,0,3.14-.66,9.52,9.52,0,0,0,2.59-2Zm32.53-25V73.61h-3.6a1.39,1.39,0,0,1-1.48-1.07l-.5-2.36a12.4,12.4,0,0,1-3.4,2.74,9.17,9.17,0,0,1-4.47,1,7.95,7.95,0,0,1-6.55-3.26A11.61,11.61,0,0,1,201,66.79a19.71,19.71,0,0,1-.66-5.34,16.77,16.77,0,0,1,.74-5.06,12.21,12.21,0,0,1,2.13-4,9.88,9.88,0,0,1,3.31-2.69,9.64,9.64,0,0,1,4.34-1,8.63,8.63,0,0,1,3.51.64,9,9,0,0,1,2.6,1.74V38.17ZM217,55.39a5.94,5.94,0,0,0-2.18-1.72,6.54,6.54,0,0,0-2.54-.5,5.68,5.68,0,0,0-2.41.5A4.87,4.87,0,0,0,208,55.19a7.19,7.19,0,0,0-1.17,2.57,14.83,14.83,0,0,0-.4,3.69,16.34,16.34,0,0,0,.34,3.63,7.14,7.14,0,0,0,1,2.44,3.79,3.79,0,0,0,1.58,1.36,5,5,0,0,0,2.07.41,6,6,0,0,0,3.13-.76A9.19,9.19,0,0,0,217,66.36Zm17.67-17.22V73.61h-5.89V38.17ZM245.1,62.11a11.37,11.37,0,0,0,.67,3.26,6.54,6.54,0,0,0,1.38,2.27,5.39,5.39,0,0,0,2,1.33,7.26,7.26,0,0,0,2.61.44,8.21,8.21,0,0,0,2.47-.33,11.51,11.51,0,0,0,1.81-.74c.52-.27,1-.52,1.36-.74a2.31,2.31,0,0,1,1.13-.33,1.21,1.21,0,0,1,1.1.55L261.36,70a9.45,9.45,0,0,1-2.19,1.92,12.18,12.18,0,0,1-2.54,1.24,14,14,0,0,1-2.7.66,18.78,18.78,0,0,1-2.65.19,12.93,12.93,0,0,1-4.75-.85,10.65,10.65,0,0,1-3.82-2.5,11.8,11.8,0,0,1-2.55-4.1,15.9,15.9,0,0,1-.93-5.67,13.55,13.55,0,0,1,.81-4.71,11.34,11.34,0,0,1,2.33-3.84,11,11,0,0,1,3.69-2.59,12.31,12.31,0,0,1,4.93-1,11.86,11.86,0,0,1,4.27.74,9.25,9.25,0,0,1,3.36,2.16,9.84,9.84,0,0,1,2.21,3.48,13,13,0,0,1,.8,4.71,3.82,3.82,0,0,1-.29,1.8,1.19,1.19,0,0,1-1.1.46Zm11.23-3.55A7.28,7.28,0,0,0,256,56.4a5.16,5.16,0,0,0-1-1.77,4.44,4.44,0,0,0-1.63-1.21,5.68,5.68,0,0,0-2.3-.44,5.46,5.46,0,0,0-4,1.45,7.13,7.13,0,0,0-1.87,4.13ZM112.26,14a13.72,13.72,0,0,0-19.08-.32,1.27,1.27,0,0,0-.41.93,1.31,1.31,0,0,0,.38.95l1.73,1.73a1.31,1.31,0,0,0,1.71.12,7.78,7.78,0,0,1,4.71-1.57,7.87,7.87,0,0,1,5.57,13.43C96,40.2,81.41,9.66,48.4,25.37a4.48,4.48,0,0,0-2,6.29l5.66,9.79a4.49,4.49,0,0,0,6.07,1.67l.14-.08-.11.08,2.51-1.41a57.72,57.72,0,0,0,7.91-5.89,1.37,1.37,0,0,1,1.8-.06h0a1.29,1.29,0,0,1,0,2A59.79,59.79,0,0,1,62.11,44l-.09.05-2.51,1.4a7,7,0,0,1-3.47.91,7.19,7.19,0,0,1-6.23-3.57l-5.36-9.24C34.17,40.81,27.93,54.8,31.28,72.5a1.31,1.31,0,0,0,1.29,1.06h6.09A1.3,1.3,0,0,0,40,72.42a8.94,8.94,0,0,1,17.73,0A1.3,1.3,0,0,0,59,73.56h5.94a1.31,1.31,0,0,0,1.3-1.14,8.93,8.93,0,0,1,17.72,0,1.3,1.3,0,0,0,1.29,1.14h5.87a1.3,1.3,0,0,0,1.3-1.28c.14-8.28,2.37-17.79,8.74-22.55C123.15,33.25,117.36,19.12,112.26,14ZM89.79,38.92l-4.2-2.11h0a2.64,2.64,0,1,1,4.2,2.12Z"/> </svg> </a> <div class="site-header__doc-type sr-only">User Manual</div> <div class="site-header-version"></div> <button type="button" aria-label="Navigation Menu" class="site-header__navigation-button hamburger"> <span class="hamburger__bar"></span> <span class="hamburger__bar"></span> <span class="hamburger__bar"></span> </button> </div> <div class="site-header__navigation-collapsible site-header__navigation-collapsible--collapse"> <ul class="site-header__navigation-items"> <li id="theme-toggle" class="site-header__navigation-item"> <a class="site-header__navigation-link theme-toggle" title="Theme"> <svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <title>Theme</title> <path d="m12 22c5.5228475 0 10-4.4771525 10-10s-4.4771525-10-10-10-10 4.4771525-10 10 4.4771525 10 10 10zm0-1.5v-17c4.6944204 0 8.5 3.80557963 8.5 8.5 0 4.6944204-3.8055796 8.5-8.5 8.5z"/> </svg> </a> <script type="text/javascript"> const btn = document.querySelector(".theme-toggle"); const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)"); const currentTheme = localStorage.getItem("theme"); if (currentTheme == "dark") { document.body.classList.toggle("dark-theme"); } else if (currentTheme == "light") { document.body.classList.toggle("light-theme"); } btn.addEventListener("click", function () { if (prefersDarkScheme.matches) { document.body.classList.toggle("light-theme"); var theme = document.body.classList.contains("light-theme")? "light" : "dark"; } else { document.body.classList.toggle("dark-theme"); var theme = document.body.classList.contains("dark-theme")? "dark" : "light"; } localStorage.setItem("theme", theme); }); </script> </li> <li class="site-header__navigation-item site-header__navigation-submenu-section" tabindex="0"> <span class="site-header__navigation-link"> Community </span> <div class="site-header__navigation-submenu"> <div class="site-header__navigation-submenu-item" itemprop="name"> <a target="_top" class="site-header__navigation-submenu-item-link" href="https://gradle.org/" itemprop="url"> <span class="site-header__navigation-submenu-item-link-text">Community Home</span> </a> </div> <div class="site-header__navigation-submenu-item" itemprop="name"> <a target="_top" class="site-header__navigation-submenu-item-link" href="https://discuss.gradle.org/" itemprop="url"> <span class="site-header__navigation-submenu-item-link-text">Community Forums</span> </a> </div> <div class="site-header__navigation-submenu-item" itemprop="name"> <a target="_top" class="site-header__navigation-submenu-item-link" href="https://plugins.gradle.org" itemprop="url"> <span class="site-header__navigation-submenu-item-link-text">Community Plugins</span> </a> </div> </div> </li> <li class="site-header__navigation-item" itemprop="name"> <a target="_top" class="site-header__navigation-link" href="https://dpeuniversity.gradle.com/" itemprop="url">DPE University</a> </li> <li class="site-header__navigation-item" itemprop="name"> <a target="_top" class="site-header__navigation-link" href="https://gradle.org/training/" itemprop="url">Events</a> </li> <li class="site-header__navigation-item site-header__navigation-submenu-section" tabindex="0"> <span class="site-header__navigation-link"> News </span> <div class="site-header__navigation-submenu"> <div class="site-header__navigation-submenu-item" itemprop="name"> <a class="site-header__navigation-submenu-item-link" href="https://newsletter.gradle.org" itemprop="url"> <span class="site-header__navigation-submenu-item-link-text">Newsletter</span> </a> </div> <div class="site-header__navigation-submenu-item" itemprop="name"> <a class="site-header__navigation-submenu-item-link" href="https://blog.gradle.org" itemprop="url"> <span class="site-header__navigation-submenu-item-link-text">Blog</span> </a> </div> <div class="site-header__navigation-submenu-item"> <a class="site-header__navigation-submenu-item-link" href="https://twitter.com/gradle"> <span class="site-header__navigation-submenu-item-link-text">Twitter</span> </a> </div> </div> </li> <li class="site-header__navigation-item" itemprop="name"> <a target="_top" class="site-header__navigation-link" href="https://gradle.com/develocity" itemprop="url">Develocity</a> </li> <li class="site-header__navigation-item"> <a class="site-header__navigation-link" title="Gradle on GitHub" href="https://github.com/gradle/gradle"> <svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"> <title>Github</title> <path d="M10 0C4.477 0 0 4.477 0 10c0 4.418 2.865 8.166 6.839 9.489.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.603-3.369-1.342-3.369-1.342-.454-1.155-1.11-1.462-1.11-1.462-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.831.092-.646.35-1.086.636-1.336-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.268 2.75 1.026A9.578 9.578 0 0 1 10 4.836c.85.004 1.705.114 2.504.337 1.909-1.294 2.747-1.026 2.747-1.026.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.743 0 .267.18.579.688.481C17.137 18.163 20 14.418 20 10c0-5.523-4.478-10-10-10" fill-rule="evenodd";/> </svg> </a> </li> </ul> </div> </nav> </header> <main class="main-content"> <!-- Primary Navigation --> <nav class="docs-navigation"> <div class="search-container"></div> <h3 id="overview">Overview</h3> <ul> <li><a href="../userguide/userguide.html">What is Gradle?</a></li> <li><a href="../userguide/quick_start.html">Quick Start</a></li> </ul> <h3 id="what-is-new">Releases</h3> <ul> <li><a href="https://gradle.org/releases/">All Releases</a></li> <li><a href="../release-notes.html">Release Notes</a></li> <li><a href="../userguide/installation.html">Installing Gradle</a></li> <li><a class="nav-dropdown" data-toggle="collapse" href="#upgrading-gradle" aria-expanded="false" aria-controls="upgrading-gradle">Upgrading Gradle</a> <ul id="upgrading-gradle"> <li><a href="../userguide/upgrading_version_8.html">version 8.X to latest</a></li> <li><a href="../userguide/upgrading_version_7.html">version 7.X to 8.0</a></li> <li><a href="../userguide/upgrading_version_6.html">version 6.X to 7.0</a></li> <li><a href="../userguide/upgrading_version_5.html">version 5.X to 6.0</a></li> <li><a href="../userguide/upgrading_version_4.html">version 4.X to 5.0</a></li> </ul> </li> <li><a class="nav-dropdown" data-toggle="collapse" href="#migrating-to-gradle" aria-expanded="false" aria-controls="migrating-to-gradle">Migrating to Gradle</a> <ul id="migrating-to-gradle"> <li><a href="../userguide/migrating_from_maven.html">from Maven</a></li> <li><a href="../userguide/migrating_from_ant.html">from Ant</a></li> </ul> </li> <li><a href="../userguide/troubleshooting.html">Troubleshooting</a></li> <li><a href="../userguide/compatibility.html">Compatibility Notes</a></li> <li><a href="../userguide/feature_lifecycle.html">Gradle's Feature Lifecycle</a></li> </ul> <h3 id="running-gradle-builds">Running Gradle Builds</h3> <ul> <li><a href="../userguide/getting_started_eng.html">Getting Started</a></li> <li><a class="nav-dropdown" data-toggle="collapse" href="#running-introduction" aria-expanded="false" aria-controls="introduction">Core Concepts</a> <ul id="running-introduction"> <li><a href="../userguide/gradle_basics.html">1. Gradle Basics</a></li> <li><a href="../userguide/gradle_wrapper_basics.html">2. Gradle Wrapper Basics</a></li> <li><a href="../userguide/command_line_interface_basics.html">3. Command-Line Interface Basics</a></li> <li><a href="../userguide/settings_file_basics.html">4. Settings File Basics</a></li> <li><a href="../userguide/build_file_basics.html">5. Build File Basics</a></li> <li><a href="../userguide/dependency_management_basics.html">6. Dependency Management Basics</a></li> <li><a href="../userguide/task_basics.html">7. Task Basics</a></li> <li><a href="../userguide/plugin_basics.html">8. Plugins Basics</a></li> <li><a href="../userguide/gradle_optimizations.html">9. Incremental Builds + Caching</a></li> <li><a href="../userguide/build_scans.html">10. Build Scans</a></li> </ul> </li> <li><a class="nav-dropdown" data-toggle="collapse" aria-expanded="false">Tutorial</a> <ul id="running-tutorial"> <li><a href="../userguide/part1_gradle_init.html">1. Initializing the Project</a></li> <li><a href="../userguide/part2_gradle_tasks.html">2. Running Tasks</a></li> <li><a href="../userguide/part3_gradle_dep_man.html">3. Understanding Dependencies</a></li> <li><a href="../userguide/part4_gradle_plugins.html">4. Applying Plugins</a></li> <li><a href="../userguide/part5_gradle_inc_builds.html">5. Exploring Incremental Builds</a></li> <li><a href="../userguide/part6_gradle_caching.html">6. Enabling the Build Cache</a></li> <li><a href="../userguide/part7_gradle_refs.html">7. Using Reference Materials</a></li> </ul> </li> <li><a href="../userguide/gradle_ides.html">Gradle in the IDE</a></li> <li><a href="../userguide/continuous_builds.html">Continuous Build</a></li> </ul> <h3 id="authoring-gradle-builds">Authoring Gradle Builds</h3> <ul> <li><a href="../userguide/getting_started_dev.html">Getting Started</a></li> <li><a class="nav-dropdown" data-toggle="collapse" href="#learning-the-basics" aria-expanded="false" aria-controls="learning-the-basics">Learning the Basics</a> <ul id="authoring-introduction"> <li><a href="../userguide/gradle_directories.html">1. Gradle Directories</a></li> <li><a href="../userguide/intro_multi_project_builds.html">2. Multi-Project Builds</a></li> <li><a href="../userguide/build_lifecycle.html">3. Gradle Build Lifecycle</a></li> <li><a href="../userguide/writing_settings_files.html">4. Writing Settings Files</a></li> <li><a href="../userguide/writing_build_scripts.html">5. Writing Build Scripts</a></li> <li><a href="../userguide/tutorial_using_tasks.html">6. Using Tasks</a></li> <li><a href="../userguide/writing_tasks.html">7. Writing Tasks</a></li> <li><a href="../userguide/plugins.html">8. Using Plugins</a></li> <li><a href="../userguide/writing_plugins.html">9. Writing Plugins</a></li> </ul> </li> <li><a class="nav-dropdown" data-toggle="collapse" aria-expanded="false">Tutorial</a> <ul id="authoring-tutorial"> <li><a href="../userguide/partr1_gradle_init.html">1. Initializing the Project</a></li> <li><a href="../userguide/partr2_build_lifecycle.html">2. Understanding the Build Lifecycle</a></li> <li><a href="../userguide/partr3_multi_project_builds.html">3. Multi-Project Builds</a></li> <li><a href="../userguide/partr4_settings_file.html">4. Writing the Settings File</a></li> <li><a href="../userguide/partr5_build_scripts.html">5. Writing a Build Script</a></li> <li><a href="../userguide/partr6_writing_tasks.html">6. Writing Tasks</a></li> <li><a href="../userguide/partr7_writing_plugins.html">7. Writing Plugins</a></li> </ul> </li> <li><a class="nav-dropdown" data-toggle="collapse" href="#authoring-multi-project-builds" aria-expanded="false" aria-controls="authoring-multi-project-builds">Structuring Builds</a> <ul id="authoring-multi-project-builds"> <li><a href="../userguide/multi_project_builds.html">Structuring Projects with Gradle</a></li> <li><a href="../userguide/declaring_dependencies_between_subprojects.html">Declaring Dependencies between Subprojects</a></li> <li><a href="../userguide/sharing_build_logic_between_subprojects.html">Sharing Build Logic between Subprojects</a></li> <li><a href="../userguide/composite_builds.html">Composite Builds</a></li> <li><a href="../userguide/multi_project_configuration_and_execution.html">Configuration on Demand</a></li> </ul> </li> <li><a class="nav-dropdown" data-toggle="collapse" href="#developing-tasks" aria-expanded="false" aria-controls="developing-tasks">Developing Tasks</a> <ul id="developing-tasks"> <li><a href="../userguide/more_about_tasks.html">Understanding Tasks</a></li> <li><a href="../userguide/controlling_task_execution.html">Controlling Task Execution</a></li> <li><a href="../userguide/organizing_tasks.html">Organizing Tasks</a></li> <li><a href="../userguide/implementing_custom_tasks.html">Implementing Custom Tasks</a></li> <li><a href="../userguide/lazy_configuration.html">Configuring Tasks Lazily</a></li> <li><a href="../userguide/worker_api.html">Developing Parallel Tasks</a></li> <li><a href="../userguide/custom_tasks.html">Developing Advanced Tasks</a></li> <li><a href="../userguide/build_services.html">Using Shared Build Services</a></li> </ul> </li> <li><a class="nav-dropdown" data-toggle="collapse" href="#developing-plugins" aria-expanded="false" aria-controls="developing-plugins">Developing Plugins</a> <ul id="developing-plugins"> <li><a href="../userguide/custom_plugins.html">Understanding Plugins</a></li> <li><a href="../userguide/implementing_gradle_plugins.html">Understanding Implementation Options</a></li> <li><a href="../userguide/implementing_gradle_plugins_precompiled.html">Implementing Pre-compiled Script Plugins</a></li> <li><a href="../userguide/implementing_gradle_plugins_binary.html">Implementing Binary Plugins</a></li> <li><a href="../userguide/testing_gradle_plugins.html">Testing Plugins</a></li> <li><a href="../userguide/publishing_gradle_plugins.html">Publishing Plugins</a></li> </ul> </li> <li><a class="nav-dropdown" data-toggle="collapse" href="#gradle-properties" aria-expanded="false" aria-controls="gradle-properties">Understanding Gradle Types</a> <ul id="gradle-properties-topics"> <li><a href="../userguide/properties_providers.html">Understanding Properties and Providers</a></li> <li><a href="../userguide/collections.html">Understanding Collections</a></li> <li><a href="../userguide/service_injection.html">Understanding Services and Service Injection</a></li> </ul> </li> <li><a class="nav-dropdown" data-toggle="collapse" href="#other-developing-topics" aria-expanded="false" aria-controls="other-developing-topics">Other Topics</a> <ul id="other-developing-topics"> <li><a href="../userguide/working_with_files.html">Working with Files</a></li> <li><a href="../userguide/init_scripts.html">Initialization Scripts</a></li> <li><a href="../userguide/dataflow_actions.html">Dataflow Actions</a></li> <li><a href="../userguide/test_kit.html">Testing with TestKit</a></li> <li><a href="../userguide/ant.html">Using Ant from Gradle</a></li> </ul> </li> <li><a href="../userguide/directory_layout.html">Gradle-managed Directories</a></li> <li><a href="../userguide/build_environment.html">Configuring the Build Environment</a></li> <li><a href="../userguide/logging.html">Logging with Gradle</a></li> </ul> <h3 id="managing-dependencies">Dependency Management</h3> <ul> <li><a href="../userguide/getting_started_dep_man.html">Getting Started</a></li> <li><a class="nav-dropdown" data-toggle="collapse" href="#learning-the-basics-dependency-management" aria-expanded="false" aria-controls="learning-the-basics-dependency-management">Learning the Basics</a> <ul id="learning-the-basics-dependency-management"> <li><a href="../userguide/declaring_dependencies.html">1. Declaring Dependencies</a></li> <li><a href="../userguide/dependency_configurations.html">2. Dependency Configurations</a></li> <li><a href="../userguide/declaring_repositories.html">3. Declaring Repositories</a></li> <li><a href="../userguide/centralizing_dependencies.html">4. Centralizing Dependencies</a></li> <li><a href="../userguide/dependency_constraints_conflicts.html">5. Dependency Constraints and Conflict Resolution</a></li> <li><a href="../userguide/dependency_resolution.html">6. Dependency Resolution</a></li> <li><a href="../userguide/variant_aware_resolution.html">7. Variant Aware Dependency Resolution</a></li> </ul> </li> <li><a class="nav-dropdown" data-toggle="collapse" href="#declaring-dependencies" aria-expanded="false" aria-controls="declaring-dependencies">Declaring Dependencies</a> <ul id="declaring-dependencies"> <li><a href="../userguide/declaring_dependencies_basics.html">Declaring Dependencies Basics</a></li> <li><a href="../userguide/viewing_debugging_dependencies.html">Viewing Dependencies</a></li> <li><a href="../userguide/dependency_versions.html">Declaring Versions and Ranges</a></li> <li><a href="../userguide/dependency_constraints.html">Declaring Dependency Constraints</a></li> <li><a href="../userguide/declaring_configurations.html">Creating Dependency Configurations</a></li> </ul> </li> <li><a class="nav-dropdown" data-toggle="collapse" href="#declaring-repositories" aria-expanded="false" aria-controls="declaring-repositories">Declaring Repositories</a> <ul id="declaring-repositories"> <li><a href="../userguide/declaring_repositories_basics.html">Declaring Repositories Basics</a></li> <li><a href="../userguide/centralizing_repositories.html">Centralizing Repository Declarations</a></li> <li><a href="../userguide/supported_repository_types.html">Repository Types</a></li> <li><a href="../userguide/supported_metadata_formats.html">Metadata Formats</a></li> <li><a href="../userguide/supported_repository_protocols.html">Supported Protocols</a></li> <li><a href="../userguide/filtering_repository_content.html">Filtering Repository Content</a></li> </ul> </li> <li><a class="nav-dropdown" data-toggle="collapse" href="#centralizing-dependencies" aria-expanded="false" aria-controls="centralizing-dependencies">Centralizing Dependencies</a> <ul id="centralizing-dependencies"> <li><a href="../userguide/platforms.html">Creating Platforms</a></li> <li><a href="../userguide/version_catalogs.html">Creating Version Catalogs</a></li> <li><a href="../userguide/centralizing_catalog_platform.html">Using Catalogs with Platforms</a></li> </ul> </li> <li><a class="nav-dropdown" data-toggle="collapse" href="#dependency-management" aria-expanded="false" aria-controls="dependency-management">Managing Dependencies</a> <ul id="dependency-management"> <li><a href="../userguide/dependency_locking.html">Locking Versions</a></li> <li><a href="../userguide/resolution_rules.html">Using Resolution Rules</a></li> <li><a href="../userguide/component_metadata_rules.html">Modifying Dependency Metadata</a></li> <li><a href="../userguide/dependency_caching.html">Caching Dependencies</a></li> </ul> </li> <li><a class="nav-dropdown" data-toggle="collapse" href="#understanding_dep_res" aria-expanded="false" aria-controls="understanding_dep_res">Understanding Dependency Resolution</a> <ul id="understanding_dep_res"> <li><a href="../userguide/variant_model.html">Understanding The Model</a></li> <li><a href="../userguide/component_capabilities.html">Capabilities</a></li> <li><a href="../userguide/variant_attributes.html">Variants and Attributes</a></li> </ul> </li> <li><a class="nav-dropdown" data-toggle="collapse" href="#dependency-resolution" aria-expanded="false" aria-controls="dependency-resolution">Controlling Dependency Resolution</a> <ul id="dependency-resolution"> <li><a href="../userguide/dependency_resolution_basics.html">Dependency Resolution Basics</a></li> <li><a href="../userguide/dependency_graph_resolution.html">Graph Resolution</a></li> <li><a href="../userguide/artifact_resolution.html">Artifact Resolution</a></li> <li><a href="../userguide/artifact_transforms.html">Artifact Transforms</a></li> </ul> </li> <li><a class="nav-dropdown" data-toggle="collapse" href="#publishing" aria-expanded="false" aria-controls="publishing">Publishing Libraries</a> <ul id="publishing"> <li><a href="../userguide/publishing_setup.html">Setting up Publishing</a></li> <li><a href="../userguide/publishing_gradle_module_metadata.html">Understanding Gradle Module Metadata</a></li> <li><a href="../userguide/publishing_signing.html">Signing Artifacts</a></li> <li><a href="../userguide/publishing_customization.html">Customizing Publishing</a></li> <li><a href="../userguide/publishing_maven.html">Maven Publish Plugin</a></li> <li><a href="../userguide/publishing_ivy.html">Ivy Publish Plugin</a></li> </ul> </li> <li><a class="nav-dropdown" data-toggle="collapse" href="#other" aria-expanded="false" aria-controls="other">Other Topics</a> <ul id="other"> <li><a href="../userguide/dependency_verification.html">Verifying Dependencies</a></li> <li><a href="../userguide/dependency_version_alignment.html">Aligning Dependencies</a></li> <li><a href="../userguide/feature_variants.html">Modeling Feature Variants and Optional Dependencies</a></li> </ul> </li> </ul> <h3 id="authoring-gradle-builds-java">Authoring JVM Builds</h3> <ul> <li><a href="../userguide/building_java_projects.html">Building Java & JVM projects</a></li> <li><a href="../userguide/java_testing.html">Testing Java & JVM projects</a></li> <li><a class="nav-dropdown" data-toggle="collapse" href="#java-toolchains" aria-expanded="false" aria-controls="java-toolchains">Java Toolchains</a> <ul id="java-toolchains"> <li><a href="../userguide/toolchains.html">Toolchains for JVM projects</a></li> <li><a href="../userguide/toolchain_plugins.html">Toolchain Resolver Plugins</a></li> </ul> </li> <li><a href="../userguide/dependency_management_for_java_projects.html">Managing Dependencies</a></li> <li><a class="nav-dropdown" data-toggle="collapse" href="#jvm-plugins" aria-expanded="false" aria-controls="jvm-plugins">JVM Plugins</a> <ul id="jvm-plugins"> <li><a href="../userguide/java_library_plugin.html">Java Library Plugin</a></li> <li><a href="../userguide/application_plugin.html">Java Application Plugin</a></li> <li><a href="../userguide/java_platform_plugin.html">Java Platform Plugin</a></li> <li><a href="../userguide/groovy_plugin.html">Groovy Plugin</a></li> <li><a href="../userguide/scala_plugin.html">Scala Plugin</a></li> </ul> </li> </ul> <h3 id="optimizing-build-performance">Optimizing Build Performance</h3> <ul> <li><a href="../userguide/performance.html">Improving Performance of Gradle Builds</a></li> <li><a href="../userguide/gradle_daemon.html">Gradle Daemon</a></li> <li><a href="../userguide/file_system_watching.html">File System Watching</a></li> <li><a href="../userguide/incremental_build.html">Incremental Build</a></li> <li><a class="nav-dropdown" data-toggle="collapse" href="#build-cache" aria-expanded="false" aria-controls="optimizing-build-performance">Using the Build Cache</a> <ul id="build-cache"> <li><a href="../userguide/build_cache.html">Enabling and Configuring</a></li> <li><a href="../userguide/build_cache_use_cases.html">Why use the Build Cache?</a></li> <li><a href="../userguide/build_cache_performance.html">Understanding the Impact</a></li> <li><a href="../userguide/build_cache_concepts.html">Learning Basic Concepts</a></li> <li><a href="../userguide/caching_java_projects.html">Caching Java Project</a></li> <li><a href="../userguide/caching_android_projects.html">Caching Android Project</a></li> <li><a href="../userguide/build_cache_debugging.html">Debugging Caching Issues</a></li> <li><a href="../userguide/common_caching_problems.html">Troubleshooting</a></li> </ul> </li> <li><a href="../userguide/configuration_cache.html">Using the Configuration Cache</a></li> <li><a href="../userguide/inspect.html">Inspecting Gradle Builds</a></li> <li><a href="../userguide/config_gradle.html">Configuring Gradle</a></li> <li><a href="../userguide/project_properties.html">Project Properties</a></li> <li><a href="../userguide/networking.html">Gradle Networking</a></li> </ul> <h3 id="authoring-gradle-builds-native">Authoring C++/Swift Builds</h3> <ul> <li><a href="../userguide/building_cpp_projects.html">Building C++ projects</a></li> <li><a href="../userguide/cpp_testing.html">Testing C++ projects</a></li> <li><a href="../userguide/building_swift_projects.html">Building Swift projects</a></li> <li><a href="../userguide/swift_testing.html">Testing Swift projects</a></li> </ul> <h3 id="reference">Reference</h3> <ul> <li><a class="nav-dropdown" data-toggle="collapse" href="#gradle-api" aria-expanded="false" aria-controls="gradle-api">Gradle DSLs and API</a> <ul id="gradle-api"> <li><a href="../javadoc/index.html?overview-summary.html">Javadoc</a></li> <li><a href="../userguide/groovy_build_script_primer.html">Groovy DSL Primer</a></li> <li><a href="../dsl/index.html">Groovy DSL Reference</a></li> <li><a href="../userguide/kotlin_dsl.html">Kotlin DSL Primer</a></li> <li><a href="../kotlin-dsl/index.html" target="_blank">Kotlin DSL API</a></li> <li><a href="../userguide/migrating_from_groovy_to_kotlin_dsl.html">Groovy to Kotlin DSL Migration</a></li> <li><a href="../samples/index.html">Samples</a></li> <li><a href="../userguide/glossary.html">Glossary</a></li> </ul> </li> <li><a href="https://community.gradle.org/cookbook/">Gradle Cookbook</a></li> <li><a href="../userguide/command_line_interface.html">Command-Line Interface</a></li> <li><a href="../userguide/gradle_wrapper.html">Gradle Wrapper</a></li> <li><a href="../userguide/plugin_reference.html">Core Plugins</a></li> <li id="third-party-integration"><a href="../userguide/third_party_integration.html">Gradle & Third-party Tools</a></li> <li><a href="../userguide/userguide_single.html">User Manual Single Page</a></li> <li><a href="../userguide/userguide.pdf">User Manual PDF</a></li> </ul> </nav> <!-- End Primary Navigation --> <div class="content"> <div class="chapter"> <div id="header"> <h1>Configuration cache</h1> <div class="details"> <span id="revnumber">version 8.11.1</span> </div> <div id="toc" class="toc"> <div id="toctitle">Contents</div> <ul class="sectlevel1"> <li><a href="#config_cache:intro">Introduction</a></li> <li><a href="#config_cache:usage">Using the configuration cache</a></li> <li><a href="#config_cache:stable">Stable configuration cache</a></li> <li><a href="#config_cache:ide">IDE support</a></li> <li><a href="#config_cache:plugins">Supported plugins</a></li> <li><a href="#config_cache:troubleshooting">Troubleshooting</a></li> <li><a href="#config_cache:adoption">Adoption steps</a></li> <li><a href="#config_cache:testing">Testing your build logic</a></li> <li><a href="#config_cache:requirements">Requirements</a></li> <li><a href="#config_cache:secrets">Handling of credentials and secrets</a></li> <li><a href="#config_cache:not_yet_implemented">Not yet implemented</a></li> </ul> </div> </div> <div id="content"> <div class="sect1"> <h2 id="config_cache:intro"><a class="anchor" href="#config_cache:intro"></a><a class="link" href="#config_cache:intro">Introduction</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>The configuration cache is a feature that significantly improves build performance by caching the result of the <a href="build_lifecycle.html#build_lifecycle">configuration phase</a> and reusing this for subsequent builds. Using the configuration cache, Gradle can skip the configuration phase entirely when nothing that affects the build configuration, such as build scripts, has changed. Gradle also applies performance improvements to task execution as well.</p> </div> <div class="paragraph"> <p>The configuration cache is conceptually similar to the <a href="build_cache.html#build_cache">build cache</a>, but caches different information. The build cache takes care of caching the outputs and intermediate files of the build, such as task outputs or artifact transform outputs. The configuration cache takes care of caching the build configuration for a particular set of tasks. In other words, the configuration cache saves the output of the configuration phase, and the build cache saves the outputs of the execution phase.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <i class="fa icon-important" title="Important"></i> </td> <td class="content"> <div class="paragraph"> <p>This feature is currently not enabled by default. This feature has the following limitations:</p> </div> <div class="ulist"> <ul> <li> <p>The configuration cache does not support all <a href="#config_cache:plugins:core">core Gradle plugins</a> and <a href="#config_cache:not_yet_implemented">features</a>. Full support is a work in progress.</p> </li> <li> <p>Your build and the plugins you depend on might require changes to fulfil the <a href="#config_cache:requirements">requirements</a>.</p> </li> <li> <p>IDE imports and syncs do not yet use the configuration cache.</p> </li> </ul> </div> </td> </tr> </table> </div> <div class="sect2"> <h3 id="config_cache:intro:how_does_it_work"><a class="anchor" href="#config_cache:intro:how_does_it_work"></a><a class="link" href="#config_cache:intro:how_does_it_work">How does it work?</a></h3> <div class="paragraph"> <p>When the configuration cache is enabled and you run Gradle for a particular set of tasks, for example by running <code>gradlew check</code>, Gradle checks whether a configuration cache entry is available for the requested set of tasks. If available, Gradle uses this entry instead of running the configuration phase. The cache entry contains information about the set of tasks to run, along with their configuration and dependency information.</p> </div> <div class="paragraph"> <p>The first time you run a particular set of tasks, there will be no entry in the configuration cache for these tasks and so Gradle will run the configuration phase as normal:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Run init scripts.</p> </li> <li> <p>Run the settings script for the build, applying any requested settings plugins.</p> </li> <li> <p>Configure and build the <code>buildSrc</code> project, if present.</p> </li> <li> <p>Run the builds scripts for the build, applying any requested project plugins.</p> </li> <li> <p>Calculate the task graph for the requested tasks, running any deferred configuration actions.</p> </li> </ol> </div> <div class="paragraph"> <p>Following the configuration phase, Gradle writes a snapshot of the task graph to a new configuration cache entry, for later Gradle invocations. Gradle then loads the task graph from the configuration cache, so that it can apply optimizations to the tasks, and then runs the execution phase as normal. Configuration time will still be spent the first time you run a particular set of tasks. However, you should see build performance improvement immediately because <a href="#config_cache:intro:performance_improvements">tasks will run in parallel</a>.</p> </div> <div class="paragraph"> <p>When you subsequently run Gradle with this same set of tasks, for example by running <code>gradlew check</code> again, Gradle will load the tasks and their configuration directly from the configuration cache and skip the configuration phase entirely. Before using a configuration cache entry, Gradle checks that none of the "build configuration inputs", such as build scripts, for the entry have changed. If a build configuration input has changed, Gradle will not use the entry and will run the configuration phase again as above, saving the result for later reuse.</p> </div> <div class="paragraph"> <p>Build configuration inputs include:</p> </div> <div class="ulist"> <ul> <li> <p>Init scripts</p> </li> <li> <p>Settings scripts</p> </li> <li> <p>Build scripts</p> </li> <li> <p>System properties used during the configuration phase</p> </li> <li> <p>Gradle properties used during the configuration phase</p> </li> <li> <p>Environment variables used during the configuration phase</p> </li> <li> <p>Configuration files accessed using value suppliers such as providers</p> </li> <li> <p><code>buildSrc</code> and plugin included build inputs, including build configuration inputs and source files.</p> </li> </ul> </div> <div class="paragraph"> <p>Gradle uses its own optimized serialization mechanism and format to store the configuration cache entries. It automatically serializes the state of arbitrary object graphs. If your tasks hold references to objects with simple state or of supported types you don’t have anything to do to support the serialization.</p> </div> <div class="paragraph"> <p>As a fallback and to provide some aid in migrating existing tasks, some semantics of <a href="#config_cache:not_yet_implemented:java_serialization">Java Serialization</a> are supported. But it is not recommended relying on it, mostly for performance reasons.</p> </div> </div> <div class="sect2"> <h3 id="config_cache:intro:performance_improvements"><a class="anchor" href="#config_cache:intro:performance_improvements"></a><a class="link" href="#config_cache:intro:performance_improvements">Performance improvements</a></h3> <div class="paragraph"> <p>Apart from skipping the configuration phase, the configuration cache provides some additional performance improvements:</p> </div> <div class="ulist"> <ul> <li> <p>All tasks run in parallel by default, subject to dependency constraints.</p> </li> <li> <p>Dependency resolution is cached.</p> </li> <li> <p>Configuration state and dependency resolution state is discarded from heap after writing the task graph. This reduces the peak heap usage required for a given set of tasks.</p> </li> </ul> </div> </div> <div class="sect2"> <h3 id="config_cache:in_action"><a class="anchor" href="#config_cache:in_action"></a><a class="link" href="#config_cache:in_action">Configuration caching in action</a></h3> <div class="imageblock"> <div class="content"> <img src="img/configuration-cache/running-help.gif" alt="running help"> </div> </div> </div> </div> </div> <div class="sect1"> <h2 id="config_cache:usage"><a class="anchor" href="#config_cache:usage"></a><a class="link" href="#config_cache:usage">Using the configuration cache</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>It is recommended to get started with the simplest task invocation possible. Running <code>help</code> with the configuration cache enabled is a good first step:</p> </div> <div class="listingblock"> <div class="content"> <pre>❯ gradle --configuration-cache help Calculating task graph as no cached configuration is available for tasks: help ... BUILD SUCCESSFUL in 4s 1 actionable task: 1 executed Configuration cache entry stored.</pre> </div> </div> <div class="paragraph"> <p>Running this for the first time, the configuration phase executes, calculating the task graph.</p> </div> <div class="paragraph"> <p>Then, run the same command again. This reuses the cached configuration:</p> </div> <div class="listingblock"> <div class="content"> <pre>❯ gradle --configuration-cache help Reusing configuration cache. ... BUILD SUCCESSFUL in 500ms 1 actionable task: 1 executed Configuration cache entry reused.</pre> </div> </div> <div class="paragraph"> <p>If it succeeds on your build, congratulations, you can now try with more useful tasks. You should target your development loop. A good example is running tests after making incremental changes.</p> </div> <div class="paragraph"> <p>If any problem is found caching or reusing the configuration, an HTML report is generated to help you diagnose and fix the issues. The report also shows detected build configuration inputs like system properties, environment variables and value suppliers read during the configuration phase. See the <a href="#config_cache:troubleshooting">Troubleshooting</a> section below for more information.</p> </div> <div class="paragraph"> <p>Keep reading to learn how to tweak the configuration cache, manually invalidate the state if something goes wrong and use the configuration cache from an IDE.</p> </div> <div class="sect2"> <h3 id="config_cache:usage:enable"><a class="anchor" href="#config_cache:usage:enable"></a><a class="link" href="#config_cache:usage:enable">Enabling the configuration cache</a></h3> <div class="paragraph"> <p>By default, Gradle does not use the configuration cache. To enable the cache at build time, use the <code>configuration-cache</code> flag:</p> </div> <div class="listingblock"> <div class="content"> <pre>❯ gradle --configuration-cache</pre> </div> </div> <div class="paragraph"> <p>You can also enable the cache persistently in a <code>gradle.properties</code> file using the <code>org.gradle.configuration-cache</code> property:</p> </div> <div class="listingblock"> <div class="content"> <pre class="prettyprint highlight"><code data-lang="properties">org.gradle.configuration-cache=true</code></pre> </div> </div> <div class="paragraph"> <p>If enabled in a <code>gradle.properties</code> file, you can override that setting and disable the cache at build time with the <code>no-configuration-cache</code> flag:</p> </div> <div class="listingblock"> <div class="content"> <pre>❯ gradle --no-configuration-cache</pre> </div> </div> </div> <div class="sect2"> <h3 id="config_cache:usage:ignore_problems"><a class="anchor" href="#config_cache:usage:ignore_problems"></a><a class="link" href="#config_cache:usage:ignore_problems">Ignoring problems</a></h3> <div class="paragraph"> <p>By default, Gradle will fail the build if any configuration cache problems are encountered. When gradually improving your plugin or build logic to support the configuration cache it can be useful to temporarily turn problems into warnings, with no guarantee that the build will work.</p> </div> <div class="paragraph"> <p>This can be done from the command line:</p> </div> <div class="listingblock"> <div class="content"> <pre>❯ gradle --configuration-cache-problems=warn</pre> </div> </div> <div class="paragraph"> <p>or in a <code>gradle.properties</code> file:</p> </div> <div class="listingblock"> <div class="content"> <pre class="prettyprint highlight"><code data-lang="properties">org.gradle.configuration-cache.problems=warn</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="config_cache:usage:max_problems"><a class="anchor" href="#config_cache:usage:max_problems"></a><a class="link" href="#config_cache:usage:max_problems">Allowing a maximum number of problems</a></h3> <div class="paragraph"> <p>When configuration cache problems are turned into warnings, Gradle will fail the build if <code>512</code> problems are found by default.</p> </div> <div class="paragraph"> <p>This can be adjusted by specifying an allowed maximum number of problems on the command line:</p> </div> <div class="listingblock"> <div class="content"> <pre>❯ gradle -Dorg.gradle.configuration-cache.max-problems=5</pre> </div> </div> <div class="paragraph"> <p>or in a <code>gradle.properties</code> file:</p> </div> <div class="listingblock"> <div class="content"> <pre class="prettyprint highlight"><code data-lang="properties">org.gradle.configuration-cache.max-problems=5</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="config_cache:usage:parallel"><a class="anchor" href="#config_cache:usage:parallel"></a><a class="link" href="#config_cache:usage:parallel">Enabling parallel configuration caching</a></h3> <div class="paragraph"> <p>Configuration cache storing and loading are done sequentially by default. Parallel storing and loading provide better performance, however not all builds are compatible with it.</p> </div> <div class="paragraph"> <p>To enable parallel configuration caching on the command line:</p> </div> <div class="listingblock"> <div class="content"> <pre>❯ gradle -Dorg.gradle.configuration-cache.parallel=true</pre> </div> </div> <div class="paragraph"> <p>or in a <code>gradle.properties</code> file:</p> </div> <div class="listingblock"> <div class="content"> <pre class="prettyprint highlight"><code data-lang="properties">org.gradle.configuration-cache.parallel=true</code></pre> </div> </div> <div class="paragraph"> <p>The parallel configuration caching feature is <em>incubating</em>, as not all builds are guaranteed to work correctly. Common symptoms are <code>ConcurrentModificationException</code> exceptions during the <a href="build_lifecycle.html#sec:configuration">configuration phase</a>. The feature should work well for <a href="multi_project_configuration_and_execution.html#sec:decoupled_projects">decoupled</a> multi-project builds.</p> </div> </div> <div class="sect2"> <h3 id="config_cache:usage:invalidate"><a class="anchor" href="#config_cache:usage:invalidate"></a><a class="link" href="#config_cache:usage:invalidate">Invalidating the cache</a></h3> <div class="paragraph"> <p>The configuration cache is automatically invalidated when inputs to the configuration phase change. However, certain inputs are not tracked yet, so you may have to manually invalidate the configuration cache when untracked inputs to the configuration phase change. This can happen if you <a href="#config_cache:usage:ignore_problems">ignored problems</a>. See the <a href="#config_cache:requirements">Requirements</a> and <a href="#config_cache:not_yet_implemented">Not yet implemented</a> sections below for more information.</p> </div> <div class="paragraph"> <p>The configuration cache state is stored on disk in a directory named <code>.gradle/configuration-cache</code> in the root directory of the Gradle build in use. If you need to invalidate the cache, simply delete that directory:</p> </div> <div class="listingblock"> <div class="content"> <pre>❯ rm -rf .gradle/configuration-cache</pre> </div> </div> <div class="paragraph"> <p>Configuration cache entries are checked periodically (at most every 24 hours) for whether they are still in use. They are deleted if they haven’t been used for 7 days.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="config_cache:stable"><a class="anchor" href="#config_cache:stable"></a><a class="link" href="#config_cache:stable">Stable configuration cache</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>Working towards the stabilization of configuration caching we implemented some strictness behind a feature flag when it was considered too disruptive for early adopters.</p> </div> <div class="paragraph"> <p>You can enable that feature flag as follows:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">settings.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">enableFeaturePreview("STABLE_CONFIGURATION_CACHE")</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">settings.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">enableFeaturePreview "STABLE_CONFIGURATION_CACHE"</code></pre> </div> </div> </div> </div> </div> </div> <div class="paragraph"> <p>The <code>STABLE_CONFIGURATION_CACHE</code> feature flag enables the following:</p> </div> <div class="dlist"> <dl> <dt class="hdlist1">Undeclared shared build service usage</dt> <dd> <p>When enabled, tasks using a <a href="build_services.html#build_services">shared build service</a> without declaring the requirement via the <code>Task.usesService</code> method will emit a deprecation warning.</p> </dd> </dl> </div> <div class="paragraph"> <p>In addition, when the configuration cache is not enabled but the feature flag is present, deprecations for the following <a href="#config_cache:requirements">configuration cache requirements</a> are also enabled:</p> </div> <div class="ulist"> <ul> <li> <p><a href="#config_cache:requirements:build_listeners">Registering build listeners</a></p> </li> <li> <p><a href="#config_cache:requirements:use_project_during_execution">Using the <code>Project</code> object at execution time</a></p> </li> <li> <p><a href="#config_cache:requirements:task_extensions">Using task extensions and conventions at execution time</a></p> </li> </ul> </div> <div class="paragraph"> <p>It is recommended to enable it as soon as possible in order to be ready for when we remove the flag and make the linked features the default.</p> </div> </div> </div> <div class="sect1"> <h2 id="config_cache:ide"><a class="anchor" href="#config_cache:ide"></a><a class="link" href="#config_cache:ide">IDE support</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>If you enable and configure the configuration cache from your <code>gradle.properties</code> file, then the configuration cache will be enabled when your IDE delegates to Gradle. There’s nothing more to do.</p> </div> <div class="paragraph"> <p><code>gradle.properties</code> is usually checked in to source control. If you don’t want to enable the configuration cache for your whole team yet you can also enable the configuration cache from your IDE only as explained below.</p> </div> <div class="paragraph"> <p>Note that syncing a build from an IDE doesn’t benefit from the configuration cache, only running tasks does.</p> </div> <div class="sect2"> <h3 id="config_cache:ide:intellij"><a class="anchor" href="#config_cache:ide:intellij"></a><a class="link" href="#config_cache:ide:intellij">IntelliJ based IDEs</a></h3> <div class="paragraph"> <p>In IntelliJ IDEA or Android Studio this can be done in two ways, either globally or per run configuration.</p> </div> <div class="paragraph"> <p>To enable it for the whole build, go to <code>Run > Edit configurations…​</code>. This will open the IntelliJ IDEA or Android Studio dialog to configure Run/Debug configurations. Select <code>Templates > Gradle</code> and add the necessary system properties to the <code>VM options</code> field.</p> </div> <div class="paragraph"> <p>For example to enable the configuration cache, turning problems into warnings, add the following:</p> </div> <div class="listingblock"> <div class="content"> <pre>-Dorg.gradle.configuration-cache=true -Dorg.gradle.configuration-cache.problems=warn</pre> </div> </div> <div class="paragraph"> <p>You can also choose to only enable it for a given run configuration. In this case, leave the <code>Templates > Gradle</code> configuration untouched and edit each run configuration as you see fit.</p> </div> <div class="paragraph"> <p>Combining these two ways you can enable globally and disable for certain run configurations, or the opposite.</p> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <i class="fa icon-tip" title="Tip"></i> </td> <td class="content"> <div class="paragraph"> <p>You can use the <a href="https://github.com/JetBrains/gradle-idea-ext-plugin">gradle-idea-ext-plugin</a> to configure IntelliJ run configurations from your build. This is a good way to enable the configuration cache only for the IDE.</p> </div> </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="config_cache:ide:eclipse"><a class="anchor" href="#config_cache:ide:eclipse"></a><a class="link" href="#config_cache:ide:eclipse">Eclipse IDEs</a></h3> <div class="paragraph"> <p>In Eclipse IDEs you can enable and configure the configuration cache through Buildship in two ways, either globally or per run configuration.</p> </div> <div class="paragraph"> <p>To enable it globally, go to <code>Preferences > Gradle</code>. You can use the properties described above as system properties. For example to enable the configuration cache, turning problems into warnings, add the following JVM arguments:</p> </div> <div class="ulist"> <ul> <li> <p><code>-Dorg.gradle.configuration-cache=true</code></p> </li> <li> <p><code>-Dorg.gradle.configuration-cache.problems=warn</code></p> </li> </ul> </div> <div class="paragraph"> <p>To enable it for a given run configuration, go to <code>Run configurations…​</code>, find the one you want to change, go to <code>Project Settings</code>, tick the <code>Override project settings</code> checkbox and add the same system properties as a <code>JVM argument</code>.</p> </div> <div class="paragraph"> <p>Combining these two ways you can enable globally and disable for certain run configurations, or the opposite.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="config_cache:plugins"><a class="anchor" href="#config_cache:plugins"></a><a class="link" href="#config_cache:plugins">Supported plugins</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>The configuration cache is brand new and introduces new requirements for plugin implementations. As a result, both core Gradle plugins, and community plugins need to be adjusted. This section provides information about the current support in <a href="#config_cache:plugins:core">core Gradle plugins</a> and <a href="#config_cache:plugins:community">community plugins</a>.</p> </div> <div class="sect2"> <h3 id="config_cache:plugins:core"><a class="anchor" href="#config_cache:plugins:core"></a><a class="link" href="#config_cache:plugins:core">Core Gradle plugins</a></h3> <div class="paragraph"> <p>Not all <a href="plugin_reference.html#plugin_reference">core Gradle plugins</a> support configuration caching yet.</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 33.3333%;"> <col style="width: 33.3333%;"> <col style="width: 33.3334%;"> </colgroup> <tbody> <tr> <th class="tableblock halign-left valign-top"><p class="tableblock">JVM languages and frameworks</p></th> <th class="tableblock halign-left valign-top"><p class="tableblock">Native languages</p></th> <th class="tableblock halign-left valign-top"><p class="tableblock">Packaging and distribution</p></th> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="hdlist"> <table> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13457"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="java_plugin.html#java_plugin">Java</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13458"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="java_library_plugin.html#java_library_plugin">Java Library</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13459"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="java_platform_plugin.html#java_platform_plugin">Java Platform</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13460"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="groovy_plugin.html#groovy_plugin">Groovy</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13461"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="scala_plugin.html#scala_plugin">Scala</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13462"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="antlr_plugin.html#antlr_plugin">ANTLR</a></p> </td> </tr> </table> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="hdlist"> <table> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/30806"><span class="yellow">⚠</span></a> </td> <td class="hdlist2"> <p><a href="cpp_application_plugin.html#cpp_application_plugin">C++ Application</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/30806"><span class="yellow">⚠</span></a> </td> <td class="hdlist2"> <p><a href="cpp_library_plugin.html#cpp_library_plugin">C++ Library</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13514"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="cpp_unit_test_plugin.html#cpp_unit_test_plugin">C++ Unit Test</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13515"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="swift_application_plugin.html#swift_application_plugin">Swift Application</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13487"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="swift_library_plugin.html#swift_library_plugin">Swift Library</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13488"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="xctest_plugin.html#xctest_plugin">XCTest</a></p> </td> </tr> </table> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="hdlist"> <table> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13463"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="application_plugin.html#application_plugin">Application</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13466"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="war_plugin.html#war_plugin">WAR</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13467"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="ear_plugin.html#ear_plugin">EAR</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/24329"><span class="yellow">⚠</span></a> </td> <td class="hdlist2"> <p><a href="publishing_maven.html#publishing_maven">Maven Publish</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/24328"><span class="yellow">⚠</span></a> </td> <td class="hdlist2"> <p><a href="publishing_ivy.html#publishing_ivy">Ivy Publish</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13464"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="distribution_plugin.html#distribution_plugin">Distribution</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13465"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="java_library_distribution_plugin.html#java_library_distribution_plugin">Java Library Distribution</a></p> </td> </tr> </table> </div></div></td> </tr> <tr> <th class="tableblock halign-left valign-top"><p class="tableblock"> Code analysis</p></th> <th class="tableblock halign-left valign-top"><p class="tableblock"> IDE project files generation</p></th> <th class="tableblock halign-left valign-top"><p class="tableblock">Utility</p></th> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="hdlist"> <table> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13475"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="checkstyle_plugin.html#checkstyle_plugin">Checkstyle</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13478"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="codenarc_plugin.html#codenarc_plugin">CodeNarc</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13477"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="jacoco_plugin.html#jacoco_plugin">JaCoCo</a></p> </td> </tr> <tr> <td class="hdlist1"> <span class="green">✓</span> </td> <td class="hdlist2"> <p><a href="jacoco_report_aggregation_plugin.html#jacoco_report_aggregation_plugin">JaCoCo Report Aggregation</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13476"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="pmd_plugin.html#pmd_plugin">PMD</a></p> </td> </tr> <tr> <td class="hdlist1"> <span class="green">✓</span> </td> <td class="hdlist2"> <p><a href="test_report_aggregation_plugin.html#test_report_aggregation_plugin">Test Report Aggregation</a></p> </td> </tr> </table> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="hdlist"> <table> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13479"><span class="red">✖</span></a> </td> <td class="hdlist2"> <p><a href="eclipse_plugin.html#eclipse_plugin">Eclipse</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13480"><span class="red">✖</span></a> </td> <td class="hdlist2"> <p><a href="idea_plugin.html#idea_plugin">IntelliJ IDEA</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13482"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="visual_studio_plugin.html#visual_studio_plugin">Visual Studio</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13483"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="xcode_plugin.html#xcode_plugin">Xcode</a></p> </td> </tr> </table> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="hdlist"> <table> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13455"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="base_plugin.html#base_plugin">Base</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13456"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="build_init_plugin.html#build_init_plugin">Build Init</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13470"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="signing_plugin.html#signing_plugin">Signing</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/24537"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="java_gradle_plugin.html#java_gradle_plugin">Java Plugin Development</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/23029"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="implementing_gradle_plugins_precompiled.html#implemention_precompiled_plugins">Groovy DSL Plugin Development</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13472"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="kotlin_dsl.html#sec:kotlin-dsl_plugin">Kotlin DSL Plugin Development</a></p> </td> </tr> <tr> <td class="hdlist1"> <a href="https://github.com/gradle/gradle/issues/13473"><span class="green">✓</span></a> </td> <td class="hdlist2"> <p><a href="project_report_plugin.html#project_report_plugin">Project Report Plugin</a></p> </td> </tr> </table> </div></div></td> </tr> </tbody> </table> <div class="hdlist"> <table> <tr> <td class="hdlist1"> <span class="green">✓</span> </td> <td class="hdlist2"> <p>Supported plugin</p> </td> </tr> <tr> <td class="hdlist1"> <span class="yellow">⚠</span> </td> <td class="hdlist2"> <p>Partially supported plugin</p> </td> </tr> <tr> <td class="hdlist1"> <span class="red">✖</span> </td> <td class="hdlist2"> <p>Unsupported plugin</p> </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="config_cache:plugins:community"><a class="anchor" href="#config_cache:plugins:community"></a><a class="link" href="#config_cache:plugins:community">Community plugins</a></h3> <div class="paragraph"> <p>Please refer to issue <a href="https://github.com/gradle/gradle/issues/13490">gradle/gradle#13490</a> to learn about the status of community plugins.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="config_cache:troubleshooting"><a class="anchor" href="#config_cache:troubleshooting"></a><a class="link" href="#config_cache:troubleshooting">Troubleshooting</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>The following sections will go through some general guidelines on dealing with problems with the configuration cache. This applies to both your build logic and to your Gradle plugins.</p> </div> <div class="paragraph"> <p>Upon failure to serialize the state required to run the tasks, an HTML report of detected problems is generated. The Gradle failure output includes a clickable link to the report. This report is useful and allows you to drill down into problems, understand what is causing them.</p> </div> <div class="paragraph"> <p>Let’s look at a simple example build script that contains a couple problems:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">tasks.register("someTask") { val destination = System.getProperty("someDestination") <i class="conum" data-value="1"></i><b>(1)</b> inputs.dir("source") outputs.dir(destination) doLast { project.copy { <i class="conum" data-value="2"></i><b>(2)</b> from("source") into(destination) } } }</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">tasks.register('someTask') { def destination = System.getProperty('someDestination') <i class="conum" data-value="1"></i><b>(1)</b> inputs.dir('source') outputs.dir(destination) doLast { project.copy { <i class="conum" data-value="2"></i><b>(2)</b> from 'source' into destination } } }</code></pre> </div> </div> </div> </div> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td><a href="#config_cache:requirements:reading_sys_props_and_env_vars">A system property read at configuration time</a></td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td><a href="#config_cache:requirements:use_project_during_execution">Using the <code>Project</code> object at execution time</a></td> </tr> </table> </div> <div class="paragraph"> <p>Running that task fails and print the following in the console:</p> </div> <div class="listingblock"> <div class="content"> <pre>❯ gradle --configuration-cache someTask -DsomeDestination=dest ... * What went wrong: Configuration cache problems found in this build. 1 problem was found storing the configuration cache. - Build file 'build.gradle': line 6: invocation of 'Task.project' at execution time is unsupported. See https://docs.gradle.org/0.0.0/userguide/configuration_cache.html#config_cache:requirements:use_project_during_execution See the complete report at file:///home/user/gradle/samples/build/reports/configuration-cache/<hash>/configuration-cache-report.html > Invocation of 'Task.project' by task ':someTask' at execution time is unsupported. * Try: > Run with --stacktrace option to get the stack trace. > Run with --info or --debug option to get more log output. > Run with --scan to get full insights. > Get more help at https://help.gradle.org. BUILD FAILED in 0s 1 actionable task: 1 executed Configuration cache entry discarded with 1 problem.</pre> </div> </div> <div class="paragraph"> <p>The configuration cache entry was discarded because of the found problem failing the build.</p> </div> <div class="paragraph"> <p>Details can be found in the linked HTML report:</p> </div> <div class="imageblock"> <div class="content"> <img src="img/configuration-cache/problems-report.png" alt="problems report"> </div> </div> <div class="paragraph"> <p>The report displays the set of problems twice. First grouped by problem message, then grouped by task. The former allows you to quickly see what classes of problems your build is facing. The latter allows you to quickly see which tasks are problematic. In both cases you can expand the tree in order to discover where the culprit is in the object graph.</p> </div> <div class="paragraph"> <p>The report also includes a list of detected build configuration inputs, such as environment variables, system properties and value suppliers that were read at configuration phase:</p> </div> <div class="imageblock"> <div class="content"> <img src="img/configuration-cache/inputs-report.png" alt="inputs report"> </div> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <i class="fa icon-tip" title="Tip"></i> </td> <td class="content"> <div class="paragraph"> <p>Problems displayed in the report have links to the corresponding <a href="#config_cache:requirements">requirement</a> where you can find guidance on how to fix the problem or to the corresponding <a href="#config_cache:not_yet_implemented">not yet implemented</a> feature.</p> </div> <div class="paragraph"> <p>When changing your build or plugin to fix the problems you should consider <a href="#config_cache:testing">testing your build logic with TestKit</a>.</p> </div> </td> </tr> </table> </div> <div class="paragraph"> <p>At this stage, you can decide to either <a href="#config_cache:usage:ignore_problems">turn the problems into warnings</a> and continue exploring how your build reacts to the configuration cache, or fix the problems at hand.</p> </div> <div class="paragraph"> <p>Let’s ignore the reported problem, and run the same build again twice to see what happens when reusing the cached problematic configuration:</p> </div> <div class="listingblock"> <div class="content"> <pre>❯ gradle --configuration-cache --configuration-cache-problems=warn someTask -DsomeDestination=dest Calculating task graph as no cached configuration is available for tasks: someTask > Task :someTask 1 problem was found storing the configuration cache. - Build file 'build.gradle': line 6: invocation of 'Task.project' at execution time is unsupported. See https://docs.gradle.org/0.0.0/userguide/configuration_cache.html#config_cache:requirements:use_project_during_execution See the complete report at file:///home/user/gradle/samples/build/reports/configuration-cache/<hash>/configuration-cache-report.html BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed Configuration cache entry stored with 1 problem. ❯ gradle --configuration-cache --configuration-cache-problems=warn someTask -DsomeDestination=dest Reusing configuration cache. > Task :someTask 1 problem was found reusing the configuration cache. - Build file 'build.gradle': line 6: invocation of 'Task.project' at execution time is unsupported. See https://docs.gradle.org/0.0.0/userguide/configuration_cache.html#config_cache:requirements:use_project_during_execution See the complete report at file:///home/user/gradle/samples/build/reports/configuration-cache/<hash>/configuration-cache-report.html BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed Configuration cache entry reused with 1 problem.</pre> </div> </div> <div class="paragraph"> <p>The two builds succeed reporting the observed problem, storing then reusing the configuration cache.</p> </div> <div class="paragraph"> <p>With the help of the links present in the console problem summary and in the HTML report we can fix our problems. Here’s a fixed version of the build script:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">abstract class MyCopyTask : DefaultTask() { <i class="conum" data-value="1"></i><b>(1)</b> @get:InputDirectory abstract val source: DirectoryProperty <i class="conum" data-value="2"></i><b>(2)</b> @get:OutputDirectory abstract val destination: DirectoryProperty <i class="conum" data-value="2"></i><b>(2)</b> @get:Inject abstract val fs: FileSystemOperations <i class="conum" data-value="3"></i><b>(3)</b> @TaskAction fun action() { fs.copy { <i class="conum" data-value="3"></i><b>(3)</b> from(source) into(destination) } } } tasks.register<MyCopyTask>("someTask") { val projectDir = layout.projectDirectory source = projectDir.dir("source") destination = projectDir.dir(System.getProperty("someDestination")) }</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">abstract class MyCopyTask extends DefaultTask { <i class="conum" data-value="1"></i><b>(1)</b> @InputDirectory abstract DirectoryProperty getSource() <i class="conum" data-value="2"></i><b>(2)</b> @OutputDirectory abstract DirectoryProperty getDestination() <i class="conum" data-value="2"></i><b>(2)</b> @Inject abstract FileSystemOperations getFs() <i class="conum" data-value="3"></i><b>(3)</b> @TaskAction void action() { fs.copy { <i class="conum" data-value="3"></i><b>(3)</b> from source into destination } } } tasks.register('someTask', MyCopyTask) { def projectDir = layout.projectDirectory source = projectDir.dir('source') destination = projectDir.dir(System.getProperty('someDestination')) }</code></pre> </div> </div> </div> </div> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>We turned our ad-hoc task into a proper task class,</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>with inputs and outputs declaration,</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>and injected with the <code>FileSystemOperations</code> service, a supported replacement for <a href="#config_cache:requirements:use_project_during_execution"><code>project.copy {}</code></a>.</td> </tr> </table> </div> <div class="paragraph"> <p>Running the task twice now succeeds without reporting any problem and reuses the configuration cache on the second run:</p> </div> <div class="listingblock"> <div class="content"> <pre>❯ gradle --configuration-cache someTask -DsomeDestination=dest Calculating task graph as no cached configuration is available for tasks: someTask > Task :someTask BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed Configuration cache entry stored. ❯ gradle --configuration-cache someTask -DsomeDestination=dest Reusing configuration cache. > Task :someTask BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed Configuration cache entry reused.</pre> </div> </div> <div class="paragraph"> <p>But, what if we change the value of the system property?</p> </div> <div class="listingblock"> <div class="content"> <pre>❯ gradle --configuration-cache someTask -DsomeDestination=another Calculating task graph as configuration cache cannot be reused because system property 'someDestination' has changed. > Task :someTask BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed Configuration cache entry stored.</pre> </div> </div> <div class="paragraph"> <p>The previous configuration cache entry could not be reused, and the task graph had to be calculated and stored again. This is because we read the system property at configuration time, hence requiring Gradle to run the configuration phase again when the value of that property changes. Fixing that is as simple as obtaining the provider of the system property and wiring it to the task input, without reading it at configuration time.</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">tasks.register<MyCopyTask>("someTask") { val projectDir = layout.projectDirectory source = projectDir.dir("source") destination = projectDir.dir(providers.systemProperty("someDestination")) <i class="conum" data-value="1"></i><b>(1)</b> }</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">tasks.register('someTask', MyCopyTask) { def projectDir = layout.projectDirectory source = projectDir.dir('source') destination = projectDir.dir(providers.systemProperty('someDestination')) <i class="conum" data-value="1"></i><b>(1)</b> }</code></pre> </div> </div> </div> </div> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>We wired the system property provider directly, without reading it at configuration time.</td> </tr> </table> </div> <div class="paragraph"> <p>With this simple change in place we can run the task any number of times, change the system property value, and reuse the configuration cache:</p> </div> <div class="listingblock"> <div class="content"> <pre>❯ gradle --configuration-cache someTask -DsomeDestination=dest Calculating task graph as no cached configuration is available for tasks: someTask > Task :someTask BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed Configuration cache entry stored. ❯ gradle --configuration-cache someTask -DsomeDestination=another Reusing configuration cache. > Task :someTask BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed Configuration cache entry reused.</pre> </div> </div> <div class="paragraph"> <p>We’re now done with fixing the problems with this simple task.</p> </div> <div class="paragraph"> <p>Keep reading to learn how to adopt the configuration cache for your build or your plugins.</p> </div> <div class="sect2"> <h3 id="config_cache:task_opt_out"><a class="anchor" href="#config_cache:task_opt_out"></a><a class="link" href="#config_cache:task_opt_out">Declare a task incompatible with the configuration cache</a></h3> <div class="paragraph"> <p>It is possible to declare that a particular task is not compatible with the configuration cache via the <a href="../javadoc/org/gradle/api/Task.html#notCompatibleWithConfigurationCache-java.lang.String-">Task.notCompatibleWithConfigurationCache()</a> method.</p> </div> <div class="paragraph"> <p>Configuration cache problems found in tasks marked incompatible will no longer cause the build to fail.</p> </div> <div class="paragraph"> <p>And, when an incompatible task is scheduled to run, Gradle discards the configuration state at the end of the build. You can use this to help with migration, by temporarily opting out certain tasks that are difficult to change to work with the configuration cache.</p> </div> <div class="paragraph"> <p>Check the method documentation for more details.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="config_cache:adoption"><a class="anchor" href="#config_cache:adoption"></a><a class="link" href="#config_cache:adoption">Adoption steps</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>An important prerequisite is to keep your Gradle and plugins versions up to date. The following explores the recommended steps for a successful adoption. It applies both to builds and plugins. While going through these steps, keep in mind the HTML report and the solutions explained in the <a href="#config_cache:requirements">requirements</a> chapter below.</p> </div> <div class="dlist"> <dl> <dt class="hdlist1">Start with <code>:help</code></dt> <dd> <p>Always start by trying your build or plugin with the simplest task <code>:help</code>. This will exercise the minimal configuration phase of your build or plugin.</p> </dd> <dt class="hdlist1">Progressively target useful tasks</dt> <dd> <p>Don’t go with running <code>build</code> right away. You can also use <code>--dry-run</code> to discover more configuration time problems first.</p> <div class="paragraph"> <p>When working on a build, progressively target your development feedback loop. For example, running tests after making some changes to the source code.</p> </div> <div class="paragraph"> <p>When working on a plugin, progressively target the contributed or configured tasks.</p> </div> </dd> <dt class="hdlist1">Explore by turning problems into warnings</dt> <dd> <p>Don’t stop at the first build failure and <a href="#config_cache:usage:ignore_problems">turn problems into warnings</a> to discover how your build and plugins behave. If a build fails, use the HTML report to reason about the reported problems related to the failure. Continue running more useful tasks.</p> <div class="paragraph"> <p>This will give you a good overview of the nature of the problems your build and plugins are facing. Remember that when turning problems into warnings you might need to <a href="#config_cache:usage:invalidate">manually invalidate the cache</a> in case of troubles.</p> </div> </dd> <dt class="hdlist1">Step back and fix problems iteratively</dt> <dd> <p>When you feel you know enough about what needs to be fixed, take a step back and start iteratively fixing the most important problems. Use the HTML report and this documentation to help you in this journey.</p> <div class="paragraph"> <p>Start with problems reported when <em>storing</em> the configuration cache. Once fixed, you can rely on a valid cached configuration phase and move on to fixing problems reported when <em>loading</em> the configuration cache if any.</p> </div> </dd> <dt class="hdlist1">Report encountered issues</dt> <dd> <p>If you face a problem with a <a href="#config_cache:not_yet_implemented">Gradle feature</a> or with a <a href="#config_cache:plugins:core">Gradle core plugin</a> that is not covered by this documentation, please report an issue on <a href="https://github.com/gradle/gradle/issues/new/choose"><code>gradle/gradle</code></a>.</p> <div class="paragraph"> <p>If you face a problem with a community Gradle plugin, see if it is already listed at <a href="https://github.com/gradle/gradle/issues/13490">gradle/gradle#13490</a> and consider reporting the issue to the plugin’s issue tracker.</p> </div> <div class="paragraph"> <p>A good way to report such issues is by providing information such as:</p> </div> <div class="ulist"> <ul> <li> <p>a link to this very documentation,</p> </li> <li> <p>the plugin version you tried,</p> </li> <li> <p>the custom configuration of the plugin if any, or ideally a reproducer build,</p> </li> <li> <p>a description of what fails, for example problems with a given task</p> </li> <li> <p>a copy of the build failure,</p> </li> <li> <p>the self-contained <code>configuration-cache-report.html</code> file.</p> </li> </ul> </div> </dd> <dt class="hdlist1">Test, test, test</dt> <dd> <p>Consider adding tests for your build logic. See the below section on <a href="#config_cache:testing">testing your build logic</a> for the configuration cache. This will help you while iterating on the required changes and prevent future regressions.</p> </dd> <dt class="hdlist1">Roll it out to your team</dt> <dd> <p>Once you have your developer workflow working, for example running tests from the IDE, you can consider enabling it for your team. A faster turnaround when changing code and running tests could be worth it. You’ll probably want to do this as an opt-in first.</p> <div class="paragraph"> <p>If needed, turn problems into warnings and set the maximum number of allowed problems in your build <code>gradle.properties</code> file. Keep the configuration cache disabled by default. Let your team know they can opt-in by, for example, enabling the configuration cache on their IDE run configurations for the supported workflow.</p> </div> <div class="paragraph"> <p>Later on, when more workflows are working, you can flip this around. Enable the configuration cache by default, configure CI to disable it, and if required communicate the unsupported workflow(s) for which the configuration cache needs to be disabled.</p> </div> </dd> </dl> </div> <div class="sect2"> <h3 id="config_cache:adoption:reacting"><a class="anchor" href="#config_cache:adoption:reacting"></a><a class="link" href="#config_cache:adoption:reacting">Reacting to the configuration cache in the build</a></h3> <div class="paragraph"> <p>Build logic or plugin implementations can detect if the configuration cache is enabled for a given build, and react to it accordingly. The <a href="../javadoc/org/gradle/api/configuration/BuildFeature.html#getActive--">active</a> status of the configuration cache is provided in the corresponding <a href="../javadoc/org/gradle/api/configuration/BuildFeatures.html#getConfigurationCache--">build feature</a>. You can access it by <a href="service_injection.html#service_injection">injecting</a> the <a href="../javadoc/org/gradle/api/configuration/BuildFeatures.html"><code>BuildFeatures</code></a> service into your code.</p> </div> <div class="paragraph"> <p>You can use this information to configure features of your plugin differently or to disable an optional feature that is not yet compatible. Another example involves providing additional guidance for your users, should they need to adjust their setup or be informed of temporary limitations.</p> </div> </div> <div class="sect2"> <h3 id="config_cache:adoption:changes_in_behavior"><a class="anchor" href="#config_cache:adoption:changes_in_behavior"></a><a class="link" href="#config_cache:adoption:changes_in_behavior">Adopting changes in the configuration cache behavior</a></h3> <div class="paragraph"> <p>Gradle releases bring enhancements to the configuration cache, making it detect more cases of configuration logic interacting with the environment. Those changes improve the correctness of the cache by eliminating potential false cache hits. On the other hand, they impose stricter rules that plugins and build logic need to follow to be cached as often as possible.</p> </div> <div class="paragraph"> <p>Some of those configuration inputs may be considered "benign" if their results do not affect the configured tasks. Having new configuration misses because of them may be undesirable for the build users, and the suggested strategy for eliminating them is:</p> </div> <div class="ulist"> <ul> <li> <p>Identify the configuration inputs causing the invalidation of the configuration cache with the help of the <a href="#config_cache:troubleshooting">configuration cache report</a>.</p> <div class="ulist"> <ul> <li> <p>Fix undeclared configuration inputs accessed by the build logic of the project.</p> </li> <li> <p>Report issues caused by third-party plugins to the plugin maintainers, and update the plugins once they get fixed.</p> </li> </ul> </div> </li> <li> <p>For some kinds of configuration inputs, it is possible to use the opt-out options that make Gradle fall back to the earlier behavior, omitting the inputs from detection. This temporary workaround is aimed to mitigate performance issues coming from out-of-date plugins.</p> </li> </ul> </div> <div class="paragraph"> <p>It is possible to temporarily opt out of configuration input detection in the following cases:</p> </div> <div class="ulist"> <ul> <li> <p>Since Gradle 8.1, using many APIs related to the file system is correctly tracked as configuration inputs, including the file system checks, such as <code>File.exists()</code> or <code>File.isFile()</code>.</p> <div class="paragraph"> <p>For the input tracking to ignore these file system checks on the specific paths, the Gradle property <code>org.gradle.configuration-cache.inputs.unsafe.ignore.file-system-checks</code>, with the list of the paths, relative to the root project directory and separated by <code>;</code>, can be used. To ignore multiple paths, use <code>*</code> to match arbitrary strings within one segment, or <code>**</code> across segments. Paths starting with <code>~/</code> are based on the user home directory. For example:</p> </div> <div class="listingblock"> <div class="title">gradle.properties</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="properties">org.gradle.configuration-cache.inputs.unsafe.ignore.file-system-checks=\ ~/.third-party-plugin/*.lock;\ ../../externalOutputDirectory/**;\ build/analytics.json</code></pre> </div> </div> </li> <li> <p>Before Gradle 8.4, some undeclared configuration inputs that were never used in the configuration logic could still be read when the task graph was serialized by the configuration cache. However, their changes would not invalidate the configuration cache afterward. Starting with Gradle 8.4, such undeclared configuration inputs are correctly tracked.</p> <div class="paragraph"> <p>To temporarily revert to the earlier behavior, set the Gradle property <code>org.gradle.configuration-cache.inputs.unsafe.ignore.in-serialization</code> to <code>true</code>.</p> </div> </li> </ul> </div> <div class="paragraph"> <p>Ignore configuration inputs sparingly, and only if they do not affect the tasks produced by the configuration logic. The support for these options will be removed in future releases.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="config_cache:testing"><a class="anchor" href="#config_cache:testing"></a><a class="link" href="#config_cache:testing">Testing your build logic</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>The Gradle TestKit (a.k.a. just TestKit) is a library that aids in testing Gradle plugins and build logic generally. For general guidance on how to use TestKit, see the <a href="test_kit.html#test_kit">dedicated chapter</a>.</p> </div> <div class="paragraph"> <p>To enable configuration caching in your tests, you can pass the <code>--configuration-cache</code> argument to <a href="../javadoc/org/gradle/testkit/runner/GradleRunner.html">GradleRunner</a> or use one of the other methods described in <a href="#config_cache:usage:enable">Enabling the configuration cache</a>.</p> </div> <div class="paragraph"> <p>You need to run your tasks twice. Once to prime the configuration cache. Once to reuse the configuration cache.</p> </div> <div id="ex-testing-the-configuration-cache" class="exampleblock"> <div class="title">Example 1. <a href="#ex-testing-the-configuration-cache">Testing the configuration cache</a></div> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">src/test/kotlin/org/example/BuildLogicFunctionalTest.kt</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">@Test fun `my task can be loaded from the configuration cache`() { buildFile.writeText(""" plugins { id 'org.example.my-plugin' } """) runner() .withArguments("--configuration-cache", "myTask") <i class="conum" data-value="1"></i><b>(1)</b> .build() val result = runner() .withArguments("--configuration-cache", "myTask") <i class="conum" data-value="2"></i><b>(2)</b> .build() require(result.output.contains("Reusing configuration cache.")) <i class="conum" data-value="3"></i><b>(3)</b> // ... more assertions on your task behavior }</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">src/test/groovy/org/example/BuildLogicFunctionalTest.groovy</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">def "my task can be loaded from the configuration cache"() { given: buildFile << """ plugins { id 'org.example.my-plugin' } """ when: runner() .withArguments('--configuration-cache', 'myTask') <i class="conum" data-value="1"></i><b>(1)</b> .build() and: def result = runner() .withArguments('--configuration-cache', 'myTask') <i class="conum" data-value="2"></i><b>(2)</b> .build() then: result.output.contains('Reusing configuration cache.') <i class="conum" data-value="3"></i><b>(3)</b> // ... more assertions on your task behavior }</code></pre> </div> </div> </div> </div> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>First run primes the configuration cache.</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>Second run reuses the configuration cache.</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>Assert that the configuration cache gets reused.</td> </tr> </table> </div> <div class="paragraph"> <p>If problems with the configuration cache are found then Gradle will fail the build reporting the problems, and the test will fail.</p> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <i class="fa icon-tip" title="Tip"></i> </td> <td class="content"> <div class="paragraph"> <p>A good testing strategy for a Gradle plugin is to run its whole test suite with the configuration cache enabled. This requires testing the plugin with a supported Gradle version.</p> </div> <div class="paragraph"> <p>If the plugin already supports a range of Gradle versions it might already have tests for multiple Gradle versions. In that case we recommend enabling the configuration cache starting with the Gradle version that supports it.</p> </div> <div class="paragraph"> <p>If this can’t be done right away, using tests that run all tasks contributed by the plugin several times, for e.g. asserting the <code>UP_TO_DATE</code> and <code>FROM_CACHE</code> behavior, is also a good strategy.</p> </div> </td> </tr> </table> </div> </div> </div> <div class="sect1"> <h2 id="config_cache:requirements"><a class="anchor" href="#config_cache:requirements"></a><a class="link" href="#config_cache:requirements">Requirements</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>In order to capture the state of the task graph to the configuration cache and reload it again in a later build, Gradle applies certain requirements to tasks and other build logic. Each of these requirements is treated as a configuration cache "problem" and fails the build if violations are present.</p> </div> <div class="paragraph"> <p>For the most part these requirements are actually surfacing some undeclared inputs. In other words, using the configuration cache is an opt-in to more strictness, correctness and reliability for all builds.</p> </div> <div class="paragraph"> <p>The following sections describe each of the requirements and how to change your build to fix the problems.</p> </div> <div class="sect2"> <h3 id="config_cache:requirements:disallowed_types"><a class="anchor" href="#config_cache:requirements:disallowed_types"></a><a class="link" href="#config_cache:requirements:disallowed_types">Certain types must not be referenced by tasks</a></h3> <div class="paragraph"> <p>There are a number of types that task instances must not reference from their fields. The same applies to task actions as closures such as <code>doFirst {}</code> or <code>doLast {}</code>.</p> </div> <div class="paragraph"> <p>These types fall into some categories as follows:</p> </div> <div class="ulist"> <ul> <li> <p>Live JVM state types</p> </li> <li> <p>Gradle model types</p> </li> <li> <p>Dependency management types</p> </li> </ul> </div> <div class="paragraph"> <p>In all cases the reason these types are disallowed is that their state cannot easily be stored or recreated by the configuration cache.</p> </div> <div class="paragraph"> <p>Live JVM state types (e.g. <code>ClassLoader</code>, <code>Thread</code>, <code>OutputStream</code>, <code>Socket</code> etc…​) are simply disallowed. These types almost never represent a task input or output. The only exceptions are the standard streams: <code>System.in</code>, <code>System.out</code>, and <code>System.err</code>. These streams can be used, for example, as parameters to <a href="../dsl/org.gradle.api.tasks.Exec.html"><code>Exec</code></a> and <a href="../dsl/org.gradle.api.tasks.JavaExec.html"><code>JavaExec</code></a> tasks.</p> </div> <div class="paragraph"> <p>Gradle model types (e.g. <code>Gradle</code>, <code>Settings</code>, <code>Project</code>, <code>SourceSet</code>, <code>Configuration</code> etc…​) are usually used to carry some task input that should be explicitly and precisely declared instead.</p> </div> <div class="paragraph"> <p>For example, if you reference a <code>Project</code> in order to get the <code>project.version</code> at execution time, you should instead directly declare the <em>project version</em> as an input to your task using a <code>Property<String></code>. Another example would be to reference a <code>SourceSet</code> to later get the source files, the compilation classpath or the outputs of the source set. You should instead declare these as a <code>FileCollection</code> input and reference just that.</p> </div> <div class="paragraph"> <p>The same requirement applies to dependency management types with some nuances.</p> </div> <div class="paragraph"> <p>Some types, such as <code>Configuration</code> or <code>SourceDirectorySet</code>, don’t make good task input parameters, as they hold a lot of irrelevant state, and it is better to model these inputs as something more precise. We don’t intend to make these types serializable at all. For example, if you reference a <code>Configuration</code> to later get the resolved files, you should instead declare a <code>FileCollection</code> as an input to your task. In the same vein, if you reference a <code>SourceDirectorySet</code> you should instead declare a <code>FileTree</code> as an input to your task.</p> </div> <div class="paragraph"> <p>Referencing dependency resolution results is also disallowed (e.g. <code>ArtifactResolutionQuery</code>, <code>ResolvedArtifact</code>, <code>ArtifactResult</code> etc…​). For example, if you reference some <code>ResolvedComponentResult</code> instances, you should instead declare a <code>Provider<ResolvedComponentResult></code> as an input to your task. Such a provider can be obtained by invoking <code>ResolutionResult.getRootComponent()</code>. In the same vein, if you reference some <code>ResolvedArtifactResult</code> instances, you should instead use <code>ArtifactCollection.getResolvedArtifacts()</code> that returns a <code>Provider<Set<ResolvedArtifactResult>></code> that can be mapped as an input to your task. The rule of thumb is that tasks must not reference <em>resolved</em> results, but lazy specifications instead, in order to do the dependency resolution at execution time.</p> </div> <div class="paragraph"> <p>Some types, such as <code>Publication</code> or <code>Dependency</code> are not serializable, but could be. We may, if necessary, allow these to be used as task inputs directly.</p> </div> <div class="paragraph"> <p>Here’s an example of a problematic task type referencing a <code>SourceSet</code>:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">abstract class SomeTask : DefaultTask() { @get:Input lateinit var sourceSet: SourceSet <i class="conum" data-value="1"></i><b>(1)</b> @TaskAction fun action() { val classpathFiles = sourceSet.compileClasspath.files // ... } }</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">abstract class SomeTask extends DefaultTask { @Input SourceSet sourceSet <i class="conum" data-value="1"></i><b>(1)</b> @TaskAction void action() { def classpathFiles = sourceSet.compileClasspath.files // ... } }</code></pre> </div> </div> </div> </div> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>this will be reported as a problem because referencing <code>SourceSet</code> is not allowed</td> </tr> </table> </div> <div class="paragraph"> <p>The following is how it should be done instead:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">abstract class SomeTask : DefaultTask() { @get:InputFiles @get:Classpath abstract val classpath: ConfigurableFileCollection <i class="conum" data-value="1"></i><b>(1)</b> @TaskAction fun action() { val classpathFiles = classpath.files // ... } }</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">abstract class SomeTask extends DefaultTask { @InputFiles @Classpath abstract ConfigurableFileCollection getClasspath() <i class="conum" data-value="1"></i><b>(1)</b> @TaskAction void action() { def classpathFiles = classpath.files // ... } }</code></pre> </div> </div> </div> </div> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>no more problems reported, we now reference the supported type <code>FileCollection</code></td> </tr> </table> </div> <div class="paragraph"> <p>In the same vein, if you encounter the same problem with an ad-hoc task declared in a script as follows:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">tasks.register("someTask") { doLast { val classpathFiles = sourceSets.main.get().compileClasspath.files <i class="conum" data-value="1"></i><b>(1)</b> } }</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">tasks.register('someTask') { doLast { def classpathFiles = sourceSets.main.compileClasspath.files <i class="conum" data-value="1"></i><b>(1)</b> } }</code></pre> </div> </div> </div> </div> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>this will be reported as a problem because the <code>doLast {}</code> closure is capturing a reference to the <code>SourceSet</code></td> </tr> </table> </div> <div class="paragraph"> <p>You still need to fulfil the same requirement, that is not referencing a disallowed type. Here’s how the task declaration above can be fixed:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">tasks.register("someTask") { val classpath = sourceSets.main.get().compileClasspath <i class="conum" data-value="1"></i><b>(1)</b> doLast { val classpathFiles = classpath.files } }</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">tasks.register('someTask') { def classpath = sourceSets.main.compileClasspath <i class="conum" data-value="1"></i><b>(1)</b> doLast { def classpathFiles = classpath.files } }</code></pre> </div> </div> </div> </div> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>no more problems reported, the <code>doLast {}</code> closure now only captures <code>classpath</code> which is of the supported <code>FileCollection</code> type</td> </tr> </table> </div> <div class="paragraph"> <p>Note that sometimes the disallowed type is indirectly referenced. For example, you could have a task reference some type from a plugin that is allowed. That type could reference another allowed type that in turn references a disallowed type. The hierarchical view of the object graph provided in the HTML reports for problems should help you pinpoint the offender.</p> </div> </div> <div class="sect2"> <h3 id="config_cache:requirements:use_project_during_execution"><a class="anchor" href="#config_cache:requirements:use_project_during_execution"></a><a class="link" href="#config_cache:requirements:use_project_during_execution">Using the <code>Project</code> object</a></h3> <div class="paragraph"> <p>A task must not use any <code>Project</code> objects at execution time. This includes calling <code>Task.getProject()</code> while the task is running.</p> </div> <div class="paragraph"> <p>Some cases can be fixed in the same way as for <a href="#config_cache:requirements:disallowed_types">disallowed types</a>.</p> </div> <div class="paragraph"> <p>Often, similar things are available on both <code>Project</code> and <code>Task</code>. For example if you need a <code>Logger</code> in your task actions you should use <code>Task.logger</code> instead of <code>Project.logger</code>.</p> </div> <div class="paragraph"> <p>Otherwise, you can use <a href="service_injection.html#service_injection">injected services</a> instead of the methods of <code>Project</code>.</p> </div> <div class="paragraph"> <p>Here’s an example of a problematic task type using the <code>Project</code> object at execution time:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">abstract class SomeTask : DefaultTask() { @TaskAction fun action() { project.copy { <i class="conum" data-value="1"></i><b>(1)</b> from("source") into("destination") } } }</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">abstract class SomeTask extends DefaultTask { @TaskAction void action() { project.copy { <i class="conum" data-value="1"></i><b>(1)</b> from 'source' into 'destination' } } }</code></pre> </div> </div> </div> </div> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>this will be reported as a problem because the task action is using the <code>Project</code> object at execution time</td> </tr> </table> </div> <div class="paragraph"> <p>The following is how it should be done instead:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">abstract class SomeTask : DefaultTask() { @get:Inject abstract val fs: FileSystemOperations <i class="conum" data-value="1"></i><b>(1)</b> @TaskAction fun action() { fs.copy { from("source") into("destination") } } }</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">abstract class SomeTask extends DefaultTask { @Inject abstract FileSystemOperations getFs() <i class="conum" data-value="1"></i><b>(1)</b> @TaskAction void action() { fs.copy { from 'source' into 'destination' } } }</code></pre> </div> </div> </div> </div> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>no more problem reported, the injected <code>FileSystemOperations</code> service is supported as a replacement for <code>project.copy {}</code></td> </tr> </table> </div> <div class="paragraph"> <p>In the same vein, if you encounter the same problem with an ad-hoc task declared in a script as follows:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">tasks.register("someTask") { doLast { project.copy { <i class="conum" data-value="1"></i><b>(1)</b> from("source") into("destination") } } }</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">tasks.register('someTask') { doLast { project.copy { <i class="conum" data-value="1"></i><b>(1)</b> from 'source' into 'destination' } } }</code></pre> </div> </div> </div> </div> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>this will be reported as a problem because the task action is using the <code>Project</code> object at execution time</td> </tr> </table> </div> <div class="paragraph"> <p>Here’s how the task declaration above can be fixed:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">interface Injected { @get:Inject val fs: FileSystemOperations <i class="conum" data-value="1"></i><b>(1)</b> } tasks.register("someTask") { val injected = project.objects.newInstance<Injected>() <i class="conum" data-value="2"></i><b>(2)</b> doLast { injected.fs.copy { <i class="conum" data-value="3"></i><b>(3)</b> from("source") into("destination") } } }</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">interface Injected { @Inject FileSystemOperations getFs() <i class="conum" data-value="1"></i><b>(1)</b> } tasks.register('someTask') { def injected = project.objects.newInstance(Injected) <i class="conum" data-value="2"></i><b>(2)</b> doLast { injected.fs.copy { <i class="conum" data-value="3"></i><b>(3)</b> from 'source' into 'destination' } } }</code></pre> </div> </div> </div> </div> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>services can’t be injected directly in scripts, we need an extra type to convey the injection point</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>create an instance of the extra type using <code>project.object</code> outside the task action</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>no more problem reported, the task action references <code>injected</code> that provides the <code>FileSystemOperations</code> service, supported as a replacement for <code>project.copy {}</code></td> </tr> </table> </div> <div class="paragraph"> <p>As you can see above, fixing ad-hoc tasks declared in scripts requires quite a bit of ceremony. It is a good time to think about extracting your task declaration as a proper task class as shown previously.</p> </div> <div class="paragraph"> <p>The following table shows what APIs or injected service should be used as a replacement for each of the <code>Project</code> methods.</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 50%;"> <col style="width: 50%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Instead of:</th> <th class="tableblock halign-left valign-top">Use:</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.rootDir</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>A task input or output property or a script variable to capture the result of using <code>project.rootDir</code> to calculate the actual parameter.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.projectDir</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>A task input or output property or a script variable to capture the result of using <code>project.projectDir</code> to calculate the actual parameter.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.buildDir</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>A task input or output property or a script variable to capture the result of using <code>project.buildDir</code> to calculate the actual parameter.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.name</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>A task input or output property or a script variable to capture the result of using <code>project.name</code> to calculate the actual parameter.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.description</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>A task input or output property or a script variable to capture the result of using <code>project.description</code> to calculate the actual parameter.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.group</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>A task input or output property or a script variable to capture the result of using <code>project.group</code> to calculate the actual parameter.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.version</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>A task input or output property or a script variable to capture the result of using <code>project.version</code> to calculate the actual parameter.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.properties</code>, <code>project.property(name)</code>, <code>project.hasProperty(name)</code>, <code>project.getProperty(name)</code> or <code>project.findProperty(name)</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><a href="../dsl/org.gradle.api.provider.ProviderFactory.html#org.gradle.api.provider.ProviderFactory:gradleProperty(java.lang.String)">Value providers for Gradle properties</a>.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.logger</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><a href="../javadoc/org/gradle/api/Task.html#getLogger--">Task.logger</a></p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.provider {}</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><a href="../dsl/org.gradle.api.provider.ProviderFactory.html#org.gradle.api.provider.ProviderFactory:provider(java.util.concurrent.Callable)">ProviderFactory.provider {}</a></p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.file(path)</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>A task input or output property or a script variable to capture the result of using <code>project.file(file)</code> to calculate the actual parameter.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.uri(path)</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>A task input or output property or a script variable to capture the result of using <code>project.uri(path)</code> to calculate the actual parameter. Otherwise, <code>File.toURI()</code> or some other JVM API can be used.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.relativePath(path)</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><a href="../javadoc/org/gradle/api/file/ProjectLayout.html#getProjectDirectory--">ProjectLayout.projectDirectory</a>.<a href="../javadoc/org/gradle/api/file/Directory.html#file-java.lang.String-">file(path)</a></p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.files(paths)</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><a href="../javadoc/org/gradle/api/model/ObjectFactory.html#fileCollection--">ObjectFactory.fileCollection()</a>.<a href="../javadoc/org/gradle/api/file/ConfigurableFileCollection.html#from-java.lang.Object…​-">from(paths)</a></p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.fileTree(paths)</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><a href="../javadoc/org/gradle/api/model/ObjectFactory.html#fileTree--">ObjectFactory.fileTree()</a>.<a href="../javadoc/org/gradle/api/file/ConfigurableFileTree.html#from-java.lang.Object-">from(dir)</a></p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.zipTree(path)</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><a href="../javadoc/org/gradle/api/file/ArchiveOperations.html#zipTree-java.lang.Object-">ArchiveOperations.zipTree(path)</a></p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.tarTree(path)</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><a href="../javadoc/org/gradle/api/file/ArchiveOperations.html#tarTree-java.lang.Object-">ArchiveOperations.tarTree(path)</a></p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.resources</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>A task input or output property or a script variable to capture the result of using <code>project.resource</code> to calculate the actual parameter.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.copySpec {}</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><a href="../javadoc/org/gradle/api/file/FileSystemOperations.html#copySpec--">FileSystemOperations.copySpec {}</a></p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.copy {}</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><a href="../javadoc/org/gradle/api/file/FileSystemOperations.html#copy-org.gradle.api.Action-">FileSystemOperations.copy {}</a></p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.sync {}</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><a href="../javadoc/org/gradle/api/file/FileSystemOperations.html#sync-org.gradle.api.Action-">FileSystemOperations.sync {}</a></p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.delete {}</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><a href="../javadoc/org/gradle/api/file/FileSystemOperations.html#delete-org.gradle.api.Action-">FileSystemOperations.delete {}</a></p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.mkdir(path)</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>The Kotlin, Groovy or Java API available to your build logic.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.exec {}</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><a href="../javadoc/org/gradle/process/ExecOperations.html#exec-org.gradle.api.Action-">ExecOperations.exec {}</a></p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.javaexec {}</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><a href="../javadoc/org/gradle/process/ExecOperations.html#javaexec-org.gradle.api.Action-">ExecOperations.javaexec {}</a></p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.ant {}</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><a href="../javadoc/org/gradle/api/Task.html#getAnt--">Task.ant</a></p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><code>project.createAntBuilder()</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p><a href="../javadoc/org/gradle/api/Task.html#getAnt--">Task.ant</a></p> </div></div></td> </tr> </tbody> </table> </div> <div class="sect2"> <h3 id="config_cache:requirements:task_access"><a class="anchor" href="#config_cache:requirements:task_access"></a><a class="link" href="#config_cache:requirements:task_access">Accessing a task instance from another instance</a></h3> <div class="paragraph"> <p>Tasks should not directly access the state of another task instance. Instead, tasks should be connected using <a href="lazy_configuration.html#working_with_task_dependencies_in_lazy_properties">inputs and outputs relationships</a>.</p> </div> <div class="paragraph"> <p>Note that this requirement makes it unsupported to write tasks that configure other tasks at execution time.</p> </div> </div> <div class="sect2"> <h3 id="config_cache:requirements:shared_objects"><a class="anchor" href="#config_cache:requirements:shared_objects"></a><a class="link" href="#config_cache:requirements:shared_objects">Sharing mutable objects</a></h3> <div class="paragraph"> <p>When storing a task to the configuration cache, all objects directly or indirectly referenced through the task’s fields are serialized. In most cases, deserialization preserves reference equality: if two fields <code>a</code> and <code>b</code> reference the same instance at configuration time, then upon deserialization they will reference the same instance again, so <code>a == b</code> (or <code>a === b</code> in Groovy and Kotlin syntax) still holds. However, for performance reasons, some classes, in particular <code>java.lang.String</code>, <code>java.io.File</code>, and many implementations of <code>java.util.Collection</code> interface, are serialized without preserving the reference equality. Upon deserialization, fields that referred to the object of such a class can refer to different but equal objects.</p> </div> <div class="paragraph"> <p>Let’s look at a task that stores a user-defined object and an <code>ArrayList</code> in task fields.</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">class StateObject { // ... } abstract class StatefulTask : DefaultTask() { @get:Internal var stateObject: StateObject? = null @get:Internal var strings: List<String>? = null } tasks.register<StatefulTask>("checkEquality") { val objectValue = StateObject() val stringsValue = arrayListOf("a", "b") stateObject = objectValue strings = stringsValue doLast { <i class="conum" data-value="1"></i><b>(1)</b> println("POJO reference equality: ${stateObject === objectValue}") <i class="conum" data-value="2"></i><b>(2)</b> println("Collection reference equality: ${strings === stringsValue}") <i class="conum" data-value="3"></i><b>(3)</b> println("Collection equality: ${strings == stringsValue}") <i class="conum" data-value="4"></i><b>(4)</b> } }</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">class StateObject { // ... } abstract class StatefulTask extends DefaultTask { @Internal StateObject stateObject @Internal List<String> strings } tasks.register("checkEquality", StatefulTask) { def objectValue = new StateObject() def stringsValue = ["a", "b"] as ArrayList<String> stateObject = objectValue strings = stringsValue doLast { <i class="conum" data-value="1"></i><b>(1)</b> println("POJO reference equality: ${stateObject === objectValue}") <i class="conum" data-value="2"></i><b>(2)</b> println("Collection reference equality: ${strings === stringsValue}") <i class="conum" data-value="3"></i><b>(3)</b> println("Collection equality: ${strings == stringsValue}") <i class="conum" data-value="4"></i><b>(4)</b> } }</code></pre> </div> </div> </div> </div> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td><code>doLast</code> action captures the references from the enclosing scope. These captured references are also serialized to the configuration cache.</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>Compare the reference to an object of user-defined class stored in the task field and the reference captured in the <code>doLast</code> action.</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>Compare the reference to <code>ArrayList</code> instance stored in the task field and the reference captured in the <code>doLast</code> action.</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>Check the equality of stored and captured lists.</td> </tr> </table> </div> <div class="paragraph"> <p>Running the build without the configuration cache shows that reference equality is preserved in both cases.</p> </div> <div class="listingblock"> <div class="content"> <pre>❯ gradle --no-configuration-cache checkEquality > Task :checkEquality POJO reference equality: true Collection reference equality: true Collection equality: true</pre> </div> </div> <div class="paragraph"> <p>However, with the configuration cache enabled, only the user-defined object references are the same. List references are different, though the referenced lists are equal.</p> </div> <div class="listingblock"> <div class="content"> <pre>❯ gradle --configuration-cache checkEquality > Task :checkEquality POJO reference equality: true Collection reference equality: false Collection equality: true</pre> </div> </div> <div class="paragraph"> <p>In general, it isn’t recommended to share mutable objects between configuration and execution phases. If you need to do this, you should always wrap the state in a class you define. There is no guarantee that the reference equality is preserved for standard Java, Groovy, and Kotlin types, or for Gradle-defined types.</p> </div> <div class="paragraph"> <p>Note that no reference equality is preserved between tasks: each task is its own "realm", so it is not possible to share objects between tasks. Instead, you can use a <a href="build_services.html#build_services">build service</a> to wrap the shared state.</p> </div> </div> <div class="sect2"> <h3 id="config_cache:requirements:task_extensions"><a class="anchor" href="#config_cache:requirements:task_extensions"></a><a class="link" href="#config_cache:requirements:task_extensions">Accessing task extensions or conventions</a></h3> <div class="paragraph"> <p>Tasks should not access conventions and extensions, including extra properties, at execution time. Instead, any value that’s relevant for the execution of the task should be modeled as a task property.</p> </div> </div> <div class="sect2"> <h3 id="config_cache:requirements:build_listeners"><a class="anchor" href="#config_cache:requirements:build_listeners"></a><a class="link" href="#config_cache:requirements:build_listeners">Using build listeners</a></h3> <div class="paragraph"> <p>Plugins and build scripts must not register any build listeners. That is listeners registered at configuration time that get notified at execution time. For example a <code>BuildListener</code> or a <code>TaskExecutionListener</code>.</p> </div> <div class="paragraph"> <p>These should be replaced by <a href="build_services.html#build_services">build services</a>, registered to receive information about <a href="build_services.html#operation_listener">task execution</a> if needed. Use <a href="dataflow_actions.html#dataflow_action">dataflow actions</a> to handle the build result instead of <code>buildFinished</code> listeners.</p> </div> </div> <div class="sect2"> <h3 id="config_cache:requirements:external_processes"><a class="anchor" href="#config_cache:requirements:external_processes"></a><a class="link" href="#config_cache:requirements:external_processes">Running external processes</a></h3> <div class="paragraph"> <p>Plugin and build scripts should avoid running external processes at configuration time. In general, it is preferred to run external processes in tasks with properly declared inputs and outputs to avoid unnecessary work when the task is up-to-date. However, if needed, you should only use the configuration-cache-compatible APIs described below, instead of Java and Groovy standard APIs, or Gradle-provided methods <code>Project.exec</code>, <code>Project.javaexec</code>, <code>ExecOperations.exec</code>, and <code>ExecOperations.javaexec</code>. The flexibility of these methods prevents Gradle from determining how the calls impact the build configuration, making it difficult to ensure that the configuration cache entry can be safely reused.</p> </div> <div class="paragraph"> <p>For simpler cases, when grabbing the output of the process is enough, <a href="../dsl/org.gradle.api.provider.ProviderFactory.html#org.gradle.api.provider.ProviderFactory:exec(org.gradle.api.Action)">providers.exec()</a> and <a href="../dsl/org.gradle.api.provider.ProviderFactory.html#org.gradle.api.provider.ProviderFactory:javaexec(org.gradle.api.Action)">providers.javaexec()</a> can be used:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">val gitVersion = providers.exec { commandLine("git", "--version") }.standardOutput.asText.get()</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">def gitVersion = providers.exec { commandLine("git", "--version") }.standardOutput.asText.get()</code></pre> </div> </div> </div> </div> </div> </div> <div class="paragraph"> <p>For more complex cases a custom <a href="../javadoc/org/gradle/api/provider/ValueSource.html">ValueSource</a> implementation with injected <code>ExecOperations</code> can be used. This <code>ExecOperations</code> instance can be used at configuration time without restrictions.</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">abstract class GitVersionValueSource : ValueSource<String, ValueSourceParameters.None> { @get:Inject abstract val execOperations: ExecOperations override fun obtain(): String { val output = ByteArrayOutputStream() execOperations.exec { commandLine("git", "--version") standardOutput = output } return String(output.toByteArray(), Charset.defaultCharset()) } }</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">abstract class GitVersionValueSource implements ValueSource<String, ValueSourceParameters.None> { @Inject abstract ExecOperations getExecOperations() String obtain() { ByteArrayOutputStream output = new ByteArrayOutputStream() execOperations.exec { it.commandLine "git", "--version" it.standardOutput = output } return new String(output.toByteArray(), Charset.defaultCharset()) } }</code></pre> </div> </div> </div> </div> </div> </div> <div class="paragraph"> <p>You can also use standard Java/Kotlin/Groovy process APIs like <code>java.lang.ProcessBuilder</code> in the <code>ValueSource</code>.</p> </div> <div class="paragraph"> <p>The <code>ValueSource</code> implementation can then be used to create a provider with <a href="../dsl/org.gradle.api.provider.ProviderFactory.html#org.gradle.api.provider.ProviderFactory:of(java.lang.Class,%20org.gradle.api.Action)">providers.of</a>:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">val gitVersionProvider = providers.of(GitVersionValueSource::class) {} val gitVersion = gitVersionProvider.get()</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">def gitVersionProvider = providers.of(GitVersionValueSource.class) {} def gitVersion = gitVersionProvider.get()</code></pre> </div> </div> </div> </div> </div> </div> <div class="paragraph"> <p>In both approaches, if the value of the provider is used at configuration time then it will become a build configuration input. The external process will be executed for every build to determine if the configuration cache is up-to-date, so it is recommended to only call fast-running processes at configuration time. If the value changes then the cache is invalidated and the process will be run again during this build as part of the configuration phase.</p> </div> </div> <div class="sect2"> <h3 id="config_cache:requirements:reading_sys_props_and_env_vars"><a class="anchor" href="#config_cache:requirements:reading_sys_props_and_env_vars"></a><a class="link" href="#config_cache:requirements:reading_sys_props_and_env_vars">Reading system properties and environment variables</a></h3> <div class="paragraph"> <p>Plugins and build scripts may read system properties and environment variables directly at configuration time with standard Java, Groovy, or Kotlin APIs or with the value supplier APIs. Doing so makes such variable or property a build configuration input, so changing the value invalidates the configuration cache. The configuration cache report includes a list of these build configuration inputs to help track them.</p> </div> <div class="paragraph"> <p>In general, you should avoid reading the value of system properties and environment variables at configuration time, to avoid cache misses when value changes. Instead, you can connect the <code>Provider</code> returned by <a href="../dsl/org.gradle.api.provider.ProviderFactory.html#org.gradle.api.provider.ProviderFactory:systemProperty(java.lang.String)">providers.systemProperty()</a> or <a href="../dsl/org.gradle.api.provider.ProviderFactory.html#org.gradle.api.provider.ProviderFactory:environmentVariable(java.lang.String)">providers.environmentVariable()</a> to task properties.</p> </div> <div class="paragraph"> <p>Some access patterns that potentially enumerate all environment variables or system properties (for example, calling <code>System.getenv().forEach()</code> or using the iterator of its <code>keySet()</code>) are discouraged. In this case, Gradle cannot find out what properties are actual build configuration inputs, so every available property becomes one. Even adding a new property will invalidate the cache if this pattern is used.</p> </div> <div class="paragraph"> <p>Using a custom predicate to filter environment variables is an example of this discouraged pattern:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">val jdkLocations = System.getenv().filterKeys { it.startsWith("JDK_") }</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">def jdkLocations = System.getenv().findAll { key, _ -> key.startsWith("JDK_") }</code></pre> </div> </div> </div> </div> </div> </div> <div class="paragraph"> <p>The logic in the predicate is opaque to the configuration cache, so all environment variables are considered inputs. One way to reduce the number of inputs is to always use methods that query a concrete variable name, such as <code>getenv(String)</code>, or <code>getenv().get()</code>:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">val jdkVariables = listOf("JDK_8", "JDK_11", "JDK_17") val jdkLocations = jdkVariables.filter { v -> System.getenv(v) != null }.associate { v -> v to System.getenv(v) }</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">def jdkVariables = ["JDK_8", "JDK_11", "JDK_17"] def jdkLocations = jdkVariables.findAll { v -> System.getenv(v) != null }.collectEntries { v -> [v, System.getenv(v)] }</code></pre> </div> </div> </div> </div> </div> </div> <div class="paragraph"> <p>The fixed code above, however, is not exactly equivalent to the original as only an explicit list of variables is supported. Prefix-based filtering is a common scenario, so there are provider-based APIs to access <a href="../dsl/org.gradle.api.provider.ProviderFactory.html#org.gradle.api.provider.ProviderFactory:systemPropertiesPrefixedBy(java.lang.String)">system properties</a> and <a href="../dsl/org.gradle.api.provider.ProviderFactory.html#org.gradle.api.provider.ProviderFactory:environmentVariablesPrefixedBy(java.lang.String)">environment variables</a>:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">val jdkLocationsProvider = providers.environmentVariablesPrefixedBy("JDK_")</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">def jdkLocationsProvider = providers.environmentVariablesPrefixedBy("JDK_")</code></pre> </div> </div> </div> </div> </div> </div> <div class="paragraph"> <p>Note that the configuration cache would be invalidated not only when the value of the variable changes or the variable is removed but also when another variable with the matching prefix is added to the environment.</p> </div> <div class="paragraph"> <p>For more complex use cases a custom <a href="../javadoc/org/gradle/api/provider/ValueSource.html">ValueSource</a> implementation can be used. System properties and environment variables referenced in the code of the <code>ValueSource</code> do not become build configuration inputs, so any processing can be applied. Instead, the value of the <code>ValueSource</code> is recomputed each time the build runs and only if the value changes the configuration cache is invalidated. For example, a <code>ValueSource</code> can be used to get all environment variables with names containing the substring <code>JDK</code>:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">abstract class EnvVarsWithSubstringValueSource : ValueSource<Map<String, String>, EnvVarsWithSubstringValueSource.Parameters> { interface Parameters : ValueSourceParameters { val substring: Property<String> } override fun obtain(): Map<String, String> { return System.getenv().filterKeys { key -> key.contains(parameters.substring.get()) } } } val jdkLocationsProvider = providers.of(EnvVarsWithSubstringValueSource::class) { parameters { substring = "JDK" } }</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">abstract class EnvVarsWithSubstringValueSource implements ValueSource<Map<String, String>, Parameters> { interface Parameters extends ValueSourceParameters { Property<String> getSubstring() } Map<String, String> obtain() { return System.getenv().findAll { key, _ -> key.contains(parameters.substring.get()) } } } def jdkLocationsProvider = providers.of(EnvVarsWithSubstringValueSource.class) { parameters { substring = "JDK" } }</code></pre> </div> </div> </div> </div> </div> </div> </div> <div class="sect2"> <h3 id="config_cache:requirements:undeclared_file_read"><a class="anchor" href="#config_cache:requirements:undeclared_file_read"></a><a class="link" href="#config_cache:requirements:undeclared_file_read">Undeclared reading of files</a></h3> <div class="paragraph"> <p>Plugins and build scripts should not read files directly using the Java, Groovy or Kotlin APIs at configuration time. Instead, declare files as potential build configuration inputs using the value supplier APIs.</p> </div> <div class="paragraph"> <p>This problem is caused by build logic similar to this:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">val config = file("some.conf").readText()</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">def config = file('some.conf').text</code></pre> </div> </div> </div> </div> </div> </div> <div class="paragraph"> <p>To fix this problem, read files using <a href="../dsl/org.gradle.api.provider.ProviderFactory.html#org.gradle.api.provider.ProviderFactory:fileContents(org.gradle.api.file.RegularFile)">providers.fileContents()</a> instead:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">val config = providers.fileContents(layout.projectDirectory.file("some.conf")) .asText</code></pre> </div> </div> </div> </div> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">def config = providers.fileContents(layout.projectDirectory.file('some.conf')) .asText</code></pre> </div> </div> </div> </div> </div> </div> <div class="paragraph"> <p>In general, you should avoid reading files at configuration time, to avoid invalidating configuration cache entries when the file content changes. Instead, you can connect the <code>Provider</code> returned by <a href="../dsl/org.gradle.api.provider.ProviderFactory.html#org.gradle.api.provider.ProviderFactory:fileContents(org.gradle.api.file.RegularFile)">providers.fileContents()</a> to task properties.</p> </div> </div> <div class="sect2"> <h3 id="config_cache:requirements:bytecode_modifications_and_isolation"><a class="anchor" href="#config_cache:requirements:bytecode_modifications_and_isolation"></a><a class="link" href="#config_cache:requirements:bytecode_modifications_and_isolation">Bytecode modifications and Java agent</a></h3> <div class="paragraph"> <p>To detect the configuration inputs, Gradle modifies the bytecode of classes on the build script classpath, like plugins and their dependencies. Gradle uses a Java agent to modify the bytecode. Integrity self-checks of some libraries may fail because of the changed bytecode or the agent’s presence.</p> </div> <div class="paragraph"> <p>To work around this, you can use the <a href="worker_api.html#tasks_parallel_worker">Worker API</a> with classloader or process isolation to encapsulate the library code. The bytecode of the worker’s classpath is not modified, so the self-checks should pass. When process isolation is used, the worker action is executed in a separate worker process that doesn’t have the Gradle Java agent installed.</p> </div> <div class="paragraph"> <p>In simple cases, when the libraries also provide command-line entry points (<code>public static void main()</code> method), you can also use the <a href="../javadoc/org/gradle/api/tasks/JavaExec.html">JavaExec</a> task to isolate the library.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="config_cache:secrets"><a class="anchor" href="#config_cache:secrets"></a><a class="link" href="#config_cache:secrets">Handling of credentials and secrets</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>The configuration cache has currently no option to prevent storing secrets that are used as inputs, and so they might end up in the serialized configuration cache entry which, by default, is stored under <code>.gradle/configuration-cache</code> in your project directory.</p> </div> <div class="paragraph"> <p>To mitigate the risk of accidental exposure, Gradle encrypts the configuration cache. Gradle transparently generates a machine-specific secret key as required, caches it under the <code><a href="directory_layout.html#dir:gradle_user_home">GRADLE_USER_HOME</a></code> directory and uses it to encrypt the data in the project specific caches.</p> </div> <div class="paragraph"> <p>To enhance security further, make sure to:</p> </div> <div class="ulist"> <ul> <li> <p>secure access to configuration cache entries;</p> </li> <li> <p>leverage <code><a href="directory_layout.html#dir:gradle_user_home">GRADLE_USER_HOME</a>/gradle.properties</code> for storing secrets. The content of that file is not part of the configuration cache, only its fingerprint. If you store secrets in that file, care must be taken to protect access to the file content.</p> </li> </ul> </div> <div class="paragraph"> <p>See <a href="https://github.com/gradle/gradle/issues/22618">gradle/gradle#22618</a>.</p> </div> <div class="sect2"> <h3 id="config_cache:secrets:configuring_encryption_key"><a class="anchor" href="#config_cache:secrets:configuring_encryption_key"></a><a class="link" href="#config_cache:secrets:configuring_encryption_key">Providing an encryption key via <code>GRADLE_ENCRYPTION_KEY</code> environment variable</a></h3> <div class="paragraph"> <p>By default, Gradle automatically generates and manages the encryption key as a Java keystore stored under the <code><a href="directory_layout.html#dir:gradle_user_home">GRADLE_USER_HOME</a></code> directory.</p> </div> <div class="paragraph"> <p>For environments where this is undesirable (for instance, when the <code><a href="directory_layout.html#dir:gradle_user_home">GRADLE_USER_HOME</a></code> directory is shared across machines), you may provide Gradle with the exact encryption key to use when reading or writing the cached configuration data via the <code>GRADLE_ENCRYPTION_KEY</code> environment variable.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <i class="fa icon-important" title="Important"></i> </td> <td class="content"> <div class="paragraph"> <p>You must ensure that the same encryption key is consistently provided across multiple Gradle runs, or else Gradle will not be able to reuse existing cached configurations.</p> </div> </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="config_cache:secrets:generating_encryption_key"><a class="anchor" href="#config_cache:secrets:generating_encryption_key"></a><a class="link" href="#config_cache:secrets:generating_encryption_key">Generating an encryption key that is compatible with GRADLE_ENCRYPTION_KEY</a></h3> <div class="paragraph"> <p>For Gradle to encrypt the configuration cache using a user-specified encryption key, you must run Gradle while having the GRADLE_ENCRYPTION_KEY environment variable set with a valid AES key, encoded as a Base64 string.</p> </div> <div class="paragraph"> <p>One way of generating a Base64-encoded AES-compatible key is by using a command like this:</p> </div> <div class="listingblock"> <div class="content"> <pre class="prettyprint highlight"><code>❯ openssl rand -base64 16</code></pre> </div> </div> <div class="paragraph"> <p>This command should work on Linux, Mac OS, or on Windows, if using a tool like Cygwin.</p> </div> <div class="paragraph"> <p>You can then use the Base64-encoded key produced by that command and set it as the value of the <code>GRADLE_ENCRYPTION_KEY</code> environment variable.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="config_cache:not_yet_implemented"><a class="anchor" href="#config_cache:not_yet_implemented"></a><a class="link" href="#config_cache:not_yet_implemented">Not yet implemented</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>Support for using configuration caching with certain Gradle features is not yet implemented. Support for these features will be added in later Gradle releases.</p> </div> <div class="sect2"> <h3 id="config_cache:not_yet_implemented:sharing"><a class="anchor" href="#config_cache:not_yet_implemented:sharing"></a><a class="link" href="#config_cache:not_yet_implemented:sharing">Sharing the configuration cache</a></h3> <div class="paragraph"> <p>The configuration cache is currently stored locally only. It can be reused by hot or cold local Gradle daemons. But it can’t be shared between developers or CI machines.</p> </div> <div class="paragraph"> <p>See <a href="https://github.com/gradle/gradle/issues/13510">gradle/gradle#13510</a>.</p> </div> </div> <div class="sect2"> <h3 id="config_cache:not_yet_implemented:source_dependencies"><a class="anchor" href="#config_cache:not_yet_implemented:source_dependencies"></a><a class="link" href="#config_cache:not_yet_implemented:source_dependencies">Source dependencies</a></h3> <div class="paragraph"> <p>Support for <a href="https://blog.gradle.org/introducing-source-dependencies">source dependencies</a> is not yet implemented. With the configuration cache enabled, no problem will be reported and the build will fail.</p> </div> <div class="paragraph"> <p>See <a href="https://github.com/gradle/gradle/issues/13506">gradle/gradle#13506</a>.</p> </div> </div> <div class="sect2"> <h3 id="config_cache:not_yet_implemented:testkit_build_with_java_agent"><a class="anchor" href="#config_cache:not_yet_implemented:testkit_build_with_java_agent"></a><a class="link" href="#config_cache:not_yet_implemented:testkit_build_with_java_agent">Using a Java agent with builds run using TestKit</a></h3> <div class="paragraph"> <p>When running builds using <a href="test_kit.html#test_kit">TestKit</a>, the configuration cache can interfere with Java agents, such as the Jacoco agent, that are applied to these builds.</p> </div> <div class="paragraph"> <p>See <a href="https://github.com/gradle/gradle/issues/25979">gradle/gradle#25979</a>.</p> </div> </div> <div class="sect2"> <h3 id="config_cache:not_yet_implemented:fine_grained_tracking_of_gradle_properties"><a class="anchor" href="#config_cache:not_yet_implemented:fine_grained_tracking_of_gradle_properties"></a><a class="link" href="#config_cache:not_yet_implemented:fine_grained_tracking_of_gradle_properties">Fine-grained tracking of Gradle properties as build configuration inputs</a></h3> <div class="paragraph"> <p>Currently, all external sources of Gradle properties (<code>gradle.properties</code> in project directories and in the <code><a href="directory_layout.html#dir:gradle_user_home">GRADLE_USER_HOME</a></code>, environment variables and system properties that set properties, and properties specified with command-line flags) are considered build configuration inputs regardless of what properties are actually used at configuration time. These sources, however, are not included in the configuration cache report.</p> </div> <div class="paragraph"> <p>See <a href="https://github.com/gradle/gradle/issues/20969">gradle/gradle#20969</a>.</p> </div> </div> <div class="sect2"> <h3 id="config_cache:not_yet_implemented:java_serialization"><a class="anchor" href="#config_cache:not_yet_implemented:java_serialization"></a><a class="link" href="#config_cache:not_yet_implemented:java_serialization">Java Object Serialization</a></h3> <div class="paragraph"> <p>Gradle allows objects that support the <a href="https://docs.oracle.com/javase/8/docs/platform/serialization/spec/serialTOC.html">Java Object Serialization</a> protocol to be stored in the configuration cache.</p> </div> <div class="paragraph"> <p>The implementation is currently limited to serializable classes that either implement the <code>java.io.Externalizable</code> interface, or implement the <code>java.io.Serializable</code> interface and define one of the following combination of methods:</p> </div> <div class="ulist"> <ul> <li> <p>a <code>writeObject</code> method combined with a <code>readObject</code> method to control exactly which information to store;</p> </li> <li> <p>a <code>writeObject</code> method with no corresponding <code>readObject</code>; <code>writeObject</code> must eventually call <code>ObjectOutputStream.defaultWriteObject</code>;</p> </li> <li> <p>a <code>readObject</code> method with no corresponding <code>writeObject</code>; <code>readObject</code> must eventually call <code>ObjectInputStream.defaultReadObject</code>;</p> </li> <li> <p>a <code>writeReplace</code> method to allow the class to nominate a replacement to be written;</p> </li> <li> <p>a <code>readResolve</code> method to allow the class to nominate a replacement for the object just read;</p> </li> </ul> </div> <div class="paragraph"> <p>The following <em>Java Object Serialization</em> features are <strong>not</strong> supported:</p> </div> <div class="ulist"> <ul> <li> <p>the <code>serialPersistentFields</code> member to explicitly declare which fields are serializable; the member, if present, is ignored; the configuration cache considers all but <code>transient</code> fields serializable;</p> </li> <li> <p>the following methods of <code>ObjectOutputStream</code> are not supported and will throw <code>UnsupportedOperationException</code>:</p> <div class="ulist"> <ul> <li> <p><code>reset()</code>, <code>writeFields()</code>, <code>putFields()</code>, <code>writeChars(String)</code>, <code>writeBytes(String)</code> and <code>writeUnshared(Any?)</code>.</p> </li> </ul> </div> </li> <li> <p>the following methods of <code>ObjectInputStream</code> are not supported and will throw <code>UnsupportedOperationException</code>:</p> <div class="ulist"> <ul> <li> <p><code>readLine()</code>, <code>readFully(ByteArray)</code>, <code>readFully(ByteArray, Int, Int)</code>, <code>readUnshared()</code>, <code>readFields()</code>, <code>transferTo(OutputStream)</code> and <code>readAllBytes()</code>.</p> </li> </ul> </div> </li> <li> <p>validations registered via <code>ObjectInputStream.registerValidation</code> are simply ignored;</p> </li> <li> <p>the <code>readObjectNoData</code> method, if present, is never invoked;</p> </li> </ul> </div> <div class="paragraph"> <p>See <a href="https://github.com/gradle/gradle/issues/13588">gradle/gradle#13588</a>.</p> </div> </div> <div class="sect2"> <h3 id="config_cache:not_yet_implemented:accessing_top_level_at_execution"><a class="anchor" href="#config_cache:not_yet_implemented:accessing_top_level_at_execution"></a><a class="link" href="#config_cache:not_yet_implemented:accessing_top_level_at_execution">Accessing top-level methods and variables of a build script at execution time</a></h3> <div class="paragraph"> <p>A common approach to reuse logic and data in a build script is to extract repeating bits into top-level methods and variables. However, calling such methods at execution time is not currently supported if the configuration cache is enabled.</p> </div> <div class="paragraph"> <p>For builds scripts written in Groovy, the task fails because the method cannot be found. The following snippet uses a top-level method in the <code>listFiles</code> task:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">def dir = file('data') def listFiles(File dir) { dir.listFiles({ file -> file.isFile() } as FileFilter).name.sort() } tasks.register('listFiles') { doLast { println listFiles(dir) } }</code></pre> </div> </div> </div> </div> </div> </div> <div class="paragraph"> <p>Running the task with the configuration cache enabled produces the following error:</p> </div> <div class="listingblock"> <div class="content"> <pre>Execution failed for task ':listFiles'. > Could not find method listFiles() for arguments [/home/user/gradle/samples/data] on task ':listFiles' of type org.gradle.api.DefaultTask.</pre> </div> </div> <div class="paragraph"> <p>To prevent the task from failing, convert the referenced top-level method to a static method within a class:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="groovy">def dir = file('data') class Files { static def listFiles(File dir) { dir.listFiles({ file -> file.isFile() } as FileFilter).name.sort() } } tasks.register('listFilesFixed') { doLast { println Files.listFiles(dir) } }</code></pre> </div> </div> </div> </div> </div> </div> <div class="paragraph"> <p>Build scripts written in Kotlin cannot store tasks that reference top-level methods or variables at execution time in the configuration cache at all. This limitation exists because the captured script object references cannot be serialized. The first run of the Kotlin version of the <code>listFiles</code> task fails with the configuration cache problem.</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">val dir = file("data") fun listFiles(dir: File): List<String> = dir.listFiles { file: File -> file.isFile }.map { it.name }.sorted() tasks.register("listFiles") { doLast { println(listFiles(dir)) } }</code></pre> </div> </div> </div> </div> </div> </div> <div class="paragraph"> <p>To make the Kotlin version of this task compatible with the configuration cache, make the following changes:</p> </div> <div class="exampleblock"> <div class="content"> <div class="exampleblock testable-sample multi-language-sample"> <div class="content"> <div class="listingblock"> <div class="title">build.gradle.kts</div> <div class="content"> <pre class="prettyprint highlight"><code data-lang="kotlin">object Files { <i class="conum" data-value="1"></i><b>(1)</b> fun listFiles(dir: File): List<String> = dir.listFiles { file: File -> file.isFile }.map { it.name }.sorted() } tasks.register("listFilesFixed") { val dir = file("data") <i class="conum" data-value="2"></i><b>(2)</b> doLast { println(Files.listFiles(dir)) } }</code></pre> </div> </div> </div> </div> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>Define the method inside an object.</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>Define the variable in a smaller scope.</td> </tr> </table> </div> <div class="paragraph"> <p>See <a href="https://github.com/gradle/gradle/issues/22879">gradle/gradle#22879</a>.</p> </div> </div> <div class="sect2"> <h3 id="config_cache:not_yet_implemented:build_services_in_fingerprint"><a class="anchor" href="#config_cache:not_yet_implemented:build_services_in_fingerprint"></a><a class="link" href="#config_cache:not_yet_implemented:build_services_in_fingerprint">Using build services to invalidate the configuration cache</a></h3> <div class="paragraph"> <p>Currently, it is impossible to use a <code>BuildServiceProvider</code> or provider derived from it with <code>map</code> or <code>flatMap</code> as a parameter for the <code>ValueSource</code>, if the value of the <code>ValueSource</code> is accessed at configuration time. The same applies when such a <code>ValueSource</code> is obtained in a task that executes as part of the configuration phase, for example tasks of the <code>buildSrc</code> build or included builds contributing plugins. Note that using a <code>@ServiceReference</code> or storing <code>BuildServiceProvider</code> in an <code>@Internal</code>-annotated property of a task is safe. Generally speaking, this limitation makes it impossible to use a <code>BuildService</code> to invalidate the configuration cache.</p> </div> <div class="paragraph"> <p>See <a href="https://github.com/gradle/gradle/issues/24085">gradle/gradle#24085</a>.</p> </div> </div> </div> </div> </div> <div id="feedback-container" class="feedback-container"> <div class="feedback-buttons"> <label id="feedback-container-label"> Was this page helpful?</label> <button id="thumbs-up" onclick="showFeedbackForm(true)"> <!-- Thumbs Up SVG --> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1"> <g id="surface1"> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(10.588235%,65.882353%,79.607843%);fill-opacity:1;" d="M 3 21.375 C 3 21.582031 2.832031 21.75 2.625 21.75 C 2.417969 21.75 2.25 21.582031 2.25 21.375 C 2.25 21.167969 2.417969 21 2.625 21 C 2.832031 21 3 21.167969 3 21.375 Z M 3 21.375 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(10.588235%,65.882353%,79.607843%);fill-opacity:1;" d="M 5.25 9.75 C 5.25 8.921875 4.578125 8.25 3.75 8.25 L 1.5 8.25 C 0.671875 8.25 0 8.921875 0 9.75 L 0 22.5 C 0 23.328125 0.671875 24 1.5 24 L 3.75 24 C 4.578125 24 5.25 23.328125 5.25 22.5 Z M 2.625 22.5 C 2.003906 22.5 1.5 21.996094 1.5 21.375 C 1.5 20.753906 2.003906 20.25 2.625 20.25 C 3.246094 20.25 3.75 20.753906 3.75 21.375 C 3.75 21.996094 3.246094 22.5 2.625 22.5 Z M 2.625 22.5 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(10.588235%,65.882353%,79.607843%);fill-opacity:1;" d="M 24 10.5 C 24 9.257812 22.992188 8.25 21.75 8.25 L 15.367188 8.25 L 15.375 8.25 L 16.125 1.5 C 16.203125 0.679688 15.640625 0 14.8125 0 L 13.3125 0 C 12.375 0 11.984375 0.65625 11.625 1.5 L 8.625 8.25 C 7.816406 10.1875 6.75 10.5 6 10.5 L 6 21.832031 C 6.449219 21.9375 7.019531 22.1875 7.578125 22.761719 C 8.746094 23.960938 10.140625 24 10.875 24 L 19.5 24 C 20.742188 24 21.75 22.992188 21.75 21.75 C 21.75 21.101562 21.472656 20.515625 21.035156 20.105469 C 21.890625 19.789062 22.5 18.964844 22.5 18 C 22.5 17.351562 22.222656 16.765625 21.785156 16.355469 C 22.640625 16.039062 23.25 15.214844 23.25 14.25 C 23.25 13.601562 22.972656 13.015625 22.535156 12.605469 C 23.390625 12.289062 24 11.464844 24 10.5 Z M 24 10.5 "/> </g> </svg> </button> <button id="thumbs-down" onclick="showFeedbackForm(false)"> <!-- Thumbs Down SVG --> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1"> <g id="surface1"> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(10.588235%,65.882353%,79.607843%);fill-opacity:1;" d="M 3 13.125 C 3 13.332031 2.832031 13.5 2.625 13.5 C 2.417969 13.5 2.25 13.332031 2.25 13.125 C 2.25 12.917969 2.417969 12.75 2.625 12.75 C 2.832031 12.75 3 12.917969 3 13.125 Z M 3 13.125 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(10.588235%,65.882353%,79.607843%);fill-opacity:1;" d="M 0 1.5 C 0 0.671875 0.671875 0 1.5 0 L 3.75 0 C 4.578125 0 5.25 0.671875 5.25 1.5 L 5.25 14.25 C 5.25 15.078125 4.578125 15.75 3.75 15.75 L 1.5 15.75 C 0.671875 15.75 0 15.078125 0 14.25 Z M 2.625 14.25 C 3.246094 14.25 3.75 13.746094 3.75 13.125 C 3.75 12.503906 3.246094 12 2.625 12 C 2.003906 12 1.5 12.503906 1.5 13.125 C 1.5 13.746094 2.003906 14.25 2.625 14.25 Z M 2.625 14.25 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(10.588235%,65.882353%,79.607843%);fill-opacity:1;" d="M 24 13.5 C 24 14.742188 22.992188 15.75 21.75 15.75 L 15.367188 15.75 L 15.375 15.75 L 16.125 22.5 C 16.203125 23.320312 15.640625 24 14.8125 24 L 13.3125 24 C 12.375 24 11.984375 23.34375 11.625 22.5 L 8.625 15.75 C 7.816406 13.8125 6.75 13.5 6 13.5 L 6 2.167969 C 6.449219 2.0625 7.019531 1.8125 7.578125 1.238281 C 8.746094 0.0390625 10.140625 0 10.875 0 L 19.5 0 C 20.742188 0 21.75 1.007812 21.75 2.25 C 21.75 2.898438 21.472656 3.484375 21.035156 3.894531 C 21.890625 4.210938 22.5 5.035156 22.5 6 C 22.5 6.648438 22.222656 7.234375 21.785156 7.644531 C 22.640625 7.960938 23.25 8.785156 23.25 9.75 C 23.25 10.398438 22.972656 10.984375 22.535156 11.394531 C 23.390625 11.710938 24 12.535156 24 13.5 Z M 24 13.5 "/> </g> </svg> </button> </div> <div id="feedback-form" class="hidden-feedback-form"> <form> <label for="feedback">Additional Feedback:</label> <textarea id="feedback" name="feedback" rows="4" cols="50" placeholder="Tell us more about your experience."></textarea> <div>You can <a href="https://github.com/gradle/gradle/issues/new?assignees=&labels=a%3Adocumentation%2Cto-triage&projects=&template=40_contributor_documentation.yml"> submit issues</a> directly on Github.</div> <button id="feedback-button" type="button" onclick="submitAdditionalFeedback()" disabled> Submit Feedback <div class="animate-flicker"></div> </button> </form> </div> </div><script src="https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/run_prettify.min.js"></script> </div> <!-- end div class="chapter" --> <footer class="site-layout__footer site-footer" itemscope="itemscope" itemtype="https://schema.org/WPFooter"> <nav class="site-footer__navigation" itemtype="https://schema.org/SiteNavigationElement"> <section class="site-footer__links"> <div class="site-footer__link-group"> <header><strong>Docs</strong></header> <ul class="site-footer__links-list"> <li itemprop="name"><a href="/release-notes.html" itemprop="url">Release Notes</a></li> <li itemprop="name"><a href="/dsl/" itemprop="url">Groovy DSL</a></li> <li itemprop="name"><a href="/kotlin-dsl/" itemprop="url">Kotlin DSL</a></li> <li itemprop="name"><a href="/javadoc/" itemprop="url">Javadoc</a></li> </ul> </div> <div class="site-footer__link-group"> <header><strong>News</strong></header> <ul class="site-footer__links-list"> <li itemprop="name"><a href="https://blog.gradle.org/" itemprop="url">Blog</a></li> <li itemprop="name"><a href="https://newsletter.gradle.org/" itemprop="url">Newsletter</a></li> <li itemprop="name"><a href="https://twitter.com/gradle" itemprop="url">Twitter</a></li> <li itemprop="name"><a href="https://status.gradle.com/" itemprop="url">Status</a></li> </ul> </div> <div class="site-footer__link-group"> <header><strong>Products</strong></header> <ul class="site-footer__links-list"> <li itemprop="name"><a href="https://gradle.com/develocity/" itemprop="url">Develocity</a></li> <li itemprop="name"><a href="https://gradle.com/build-scans/" itemprop="url">Build Scan™</a></li> <li itemprop="name"><a href="https://gradle.com/build-cache/" itemprop="url">Build Cache</a></li> <li itemprop="name"><a href="https://gradle.org/services/" itemprop="url">Services</a></li> </ul> </div> <div class="site-footer__link-group"> <header><strong>Get Help</strong></header> <ul class="site-footer__links-list"> <li itemprop="name"><a href="https://discuss.gradle.org/c/help-discuss" itemprop="url">Forums</a></li> <li itemprop="name"><a href="https://github.com/gradle/" itemprop="url">GitHub</a></li> <li itemprop="name"><a href="https://gradle.org/training/" itemprop="url">Events</a></li> <li itemprop="name"><a href="https://dpeuniversity.gradle.com/" itemprop="url">DPE University</a></li> </ul> </div> </section> <section class="site-footer__subscribe-newsletter" id="newsletter-form-container"> <header class="newsletter-form__header"><h5>Stay <code>UP-TO-DATE</code> on new features and news</h5></header> <p class="disclaimer">By entering your email, you agree to our <a href="https://gradle.com/legal/terms-of-service/">Terms</a> and <a href="https://gradle.com/legal/privacy/">Privacy Policy</a>, including receipt of emails. You can unsubscribe at any time.</p> <div class="newsletter-form__container"> <form id="newsletter-form" class="newsletter-form" action="https://go.gradle.com/l/68052/2018-09-07/bk6wml" method="post"> <input id="email" class="email" name="email" type="email" placeholder="name@email.com" pattern="[^@\s]+@[^@\s]+\.[^@\s]+" maxlength="255" required=""/> <button id="submit" class="submit" type="submit">Subscribe</button> </form> </div> </section> </nav> </footer> </div> <!-- end div class="content" --> </main> <div class="site-footer-secondary"> <div class="site-footer-secondary__contents"> <div class="site-footer__copy">© <a href="https://gradle.com">Gradle Inc.</a> <time>2023</time> All rights reserved. </div> <div class="site-footer__logo"><a href="/"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 66.06"> <title>Gradle</title> <path class="cls-1" d="M85.11,4.18a14.27,14.27,0,0,0-19.83-.34,1.38,1.38,0,0,0,0,2L67,7.6a1.36,1.36,0,0,0,1.78.12A8.18,8.18,0,0,1,79.5,20.06C68.17,31.38,53.05-.36,18.73,16a4.65,4.65,0,0,0-2,6.54l5.89,10.17a4.64,4.64,0,0,0,6.3,1.73l.14-.08-.11.08L31.53,33a60.29,60.29,0,0,0,8.22-6.13,1.44,1.44,0,0,1,1.87-.06h0a1.34,1.34,0,0,1,.06,2A61.61,61.61,0,0,1,33,35.34l-.09,0-2.61,1.46a7.34,7.34,0,0,1-3.61.94,7.45,7.45,0,0,1-6.47-3.71l-5.57-9.61C4,32-2.54,46.56,1,65a1.36,1.36,0,0,0,1.33,1.11H8.61A1.36,1.36,0,0,0,10,64.87a9.29,9.29,0,0,1,18.42,0,1.35,1.35,0,0,0,1.34,1.19H35.9a1.36,1.36,0,0,0,1.34-1.19,9.29,9.29,0,0,1,18.42,0A1.36,1.36,0,0,0,57,66.06H63.1a1.36,1.36,0,0,0,1.36-1.34c.14-8.6,2.46-18.48,9.07-23.43C96.43,24.16,90.41,9.48,85.11,4.18ZM61.76,30.05l-4.37-2.19h0a2.74,2.74,0,1,1,4.37,2.2Z"/> </svg> </a></div> <div class="site-footer-secondary__links"> <a href="https://gradle.com/careers/">Careers</a> | <a href="https://gradle.com/legal/privacy/">Privacy</a> | <a href="https://gradle.com/legal/terms-of-service/">Terms of Service</a> | <a href="https://gradle.org/contact/">Contact</a> </div> </div> </div> </div> <!-- end div class="layout" --> <script type="text/javascript"> // Polyfill Element.matches() if (!Element.prototype.matches) { Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; } // Polyfill Element.closest() if (!Element.prototype.closest) { Element.prototype.closest = function (s) { var el = this; if (!document.documentElement.contains(el)) return null; do { if (typeof el.matches === "function" && el.matches(s)) return el; el = el.parentElement || el.parentNode; } while (el !== null); return null; }; } function getCurrentChapterFileName(givenUrl) { var currentChapterFileName = givenUrl.substr(givenUrl.lastIndexOf("/") + 1); if (currentChapterFileName === "index.html" || currentChapterFileName === "") { currentChapterFileName = givenUrl.substr(0, givenUrl.lastIndexOf("/")); currentChapterFileName = currentChapterFileName.substr(currentChapterFileName.lastIndexOf("/") + 1) + "/index.html"; } return currentChapterFileName; } // The media query indicating that a device is a desktop. // The `min-width: 64rem` definition should be aligned to // the one of `css/manual.css`. const desktopMediaQuery = window.matchMedia("screen and (min-width: 64rem)"); [].forEach.call(document.querySelectorAll(".docs-navigation a[href$='/" + getCurrentChapterFileName(window.location.pathname) + "']"), function (link) { // Add "active" to all links same as current URL link.classList.add("active"); // Expand all parent navigation var parentListEl = link.closest("li"); while (parentListEl !== null) { var dropDownEl = parentListEl.querySelector(".nav-dropdown"); if (dropDownEl !== null) { dropDownEl.classList.add("expanded"); } parentListEl = parentListEl.parentNode.closest("li"); } // Only scroll if the device is a desktop. // // Mobile's `docs-navigation` is always at bottom of `content`, // so we should not slide down to where `docs-navigation` lays. if (desktopMediaQuery.matches) { // Scroll to center of the page link.scrollIntoView({behavior: 'auto', block: 'center', inline: 'center'}) } }); // Expand/contract multi-level side navigation [].forEach.call(document.querySelectorAll(".docs-navigation .nav-dropdown"), function registerSideNavActions(collapsibleElement) { collapsibleElement.addEventListener("click", function toggleExpandedSideNav(evt) { evt.preventDefault(); evt.target.classList.toggle("expanded"); evt.target.setAttribute("aria-expanded", evt.target.classList.contains("expanded").toString()); return false; }, false); }); // Fix a weird issue making the initial screen always at the bottom. document.querySelector(".content").scrollIntoView(true); </script> </body> </html>