diff --git a/doc/package-lock.json b/doc/package-lock.json deleted file mode 100644 index 9153207d6cb..00000000000 --- a/doc/package-lock.json +++ /dev/null @@ -1,927 +0,0 @@ -{ - "name": "doc", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "devDependencies": { - "autoprefixer": "^10.4.22", - "postcss": "^8.5.6", - "postcss-cli": "^11.0.1" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.22", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.22.tgz", - "integrity": "sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.27.0", - "caniuse-lite": "^1.0.30001754", - "fraction.js": "^5.3.4", - "normalize-range": "^0.1.2", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.8.20", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.20.tgz", - "integrity": "sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", - "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.8.19", - "caniuse-lite": "^1.0.30001751", - "electron-to-chromium": "^1.5.238", - "node-releases": "^2.0.26", - "update-browserslist-db": "^1.1.4" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001755", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001755.tgz", - "integrity": "sha512-44V+Jm6ctPj7R52Na4TLi3Zri4dWUljJd+RDm+j8LtNCc/ihLCT+X1TzoOAkRETEWqjuLnh9581Tl80FvK7jVA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/dependency-graph": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz", - "integrity": "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.240", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.240.tgz", - "integrity": "sha512-OBwbZjWgrCOH+g6uJsA2/7Twpas2OlepS9uvByJjR2datRDuKGYeD+nP8lBBks2qnB7bGJNHDUx7c/YLaT3QMQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fraction.js": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", - "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fs-extra": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz", - "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/node-releases": { - "version": "2.0.26", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.26.tgz", - "integrity": "sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-cli": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-11.0.1.tgz", - "integrity": "sha512-0UnkNPSayHKRe/tc2YGW6XnSqqOA9eqpiRMgRlV1S6HdGi16vwJBx7lviARzbV1HpQHqLLRH3o8vTcB0cLc+5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^3.3.0", - "dependency-graph": "^1.0.0", - "fs-extra": "^11.0.0", - "picocolors": "^1.0.0", - "postcss-load-config": "^5.0.0", - "postcss-reporter": "^7.0.0", - "pretty-hrtime": "^1.0.3", - "read-cache": "^1.0.0", - "slash": "^5.0.0", - "tinyglobby": "^0.2.12", - "yargs": "^17.0.0" - }, - "bin": { - "postcss": "index.js" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-load-config": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-5.1.0.tgz", - "integrity": "sha512-G5AJ+IX0aD0dygOE0yFZQ/huFFMSNneyfp0e3/bT05a8OfPC5FUoZRPfGijUdGOJNMewJiwzcHJXFafFzeKFVA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.1.1", - "yaml": "^2.4.2" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "jiti": ">=1.21.0", - "postcss": ">=8.0.9", - "tsx": "^4.8.1" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - }, - "postcss": { - "optional": true - }, - "tsx": { - "optional": true - } - } - }, - "node_modules/postcss-reporter": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-7.1.0.tgz", - "integrity": "sha512-/eoEylGWyy6/DOiMP5lmFRdmDKThqgn7D6hP2dXKJI/0rJSO1ADFNngZfDzxL0YAxFvws+Rtpuji1YIHj4mySA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "picocolors": "^1.0.0", - "thenby": "^1.3.4" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/pretty-hrtime": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", - "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/slash": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", - "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/thenby": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/thenby/-/thenby-1.3.4.tgz", - "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", - "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - } - } -} diff --git a/lang/java/.gitignore b/lang/java/.gitignore index 5a536dfcc82..250e79d5ba7 100644 --- a/lang/java/.gitignore +++ b/lang/java/.gitignore @@ -20,3 +20,6 @@ dependency-reduced-pom.xml mapred/userlogs/ tools/userlogs/ +gradle-plugin/build +gradle-plugin/.kotlin +gradle-plugin/.gradle diff --git a/lang/java/gradle-plugin/README.md b/lang/java/gradle-plugin/README.md new file mode 100644 index 00000000000..7dbc3ca9101 --- /dev/null +++ b/lang/java/gradle-plugin/README.md @@ -0,0 +1,59 @@ +# Avro Gradle plugin (in development) + +Gradle plugin that generates Java code from Avro schemas + +## Usage + +### Add avro extension +In `build.gradle.kts`: + +Add plugin + +```kotlin +plugins { + id("eu.eventloopsoftware.avro-gradle-plugin") version "0.0.2" +} +``` +Add Avro dependency + +```kotlin +implementation("org.apache.avro:avro:1.12.1") +``` +Configure Avro Gradle plugin +```kotlin +avro { + sourceDirectory = "src/main/avro" + // All properties are available in `GradlePluginExtension.kt` +} +``` + +In `settings.gradle.kts`: + +Plugin is published on Maven Central: +```kotlin +pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + } +} +``` + + + +### Add a task hook +For Intellij to recognize the newly generated Java files add this to `build.gradle.kts`: + +```kotlin +tasks.named("compileKotlin") { dependsOn(tasks.named("avroGenerateJavaClasses")) } +``` + +### Generate Java classes + +`./gradlew avroGenerateJavaClasses` + + +## Example project that uses avro-gradle-plugin +https://codeberg.org/frevib/use-gradle-plugin-test + + diff --git a/lang/java/gradle-plugin/build.gradle.kts b/lang/java/gradle-plugin/build.gradle.kts new file mode 100644 index 00000000000..65fea48c6f1 --- /dev/null +++ b/lang/java/gradle-plugin/build.gradle.kts @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * https://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. + */ + +plugins { + kotlin("jvm") version "2.2.10" + `java-gradle-plugin` + `maven-publish` +} + +group = "org.apache.avro" +version = "1.13.0-SNAPSHOT" + +repositories { + mavenCentral() + mavenLocal() +} + +dependencies { + //implementation("org.gradle:gradle-tooling-api:7.1.1") + implementation("org.apache.avro:avro-compiler:${version}") + testImplementation(kotlin("test")) +} + +tasks.test { + useJUnitPlatform() +} +kotlin { + jvmToolchain(17) +} + +java { + withSourcesJar() +} + + +gradlePlugin { + plugins { + create("gradlePlugin") { + id = "org.apache.avro.avro-gradle-plugin" + implementationClass = "org.apache.avro.gradle.plugin.GradlePlugin" + } + } +} diff --git a/lang/java/gradle-plugin/gradle/wrapper/gradle-wrapper.jar b/lang/java/gradle-plugin/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000000..1b33c55baab Binary files /dev/null and b/lang/java/gradle-plugin/gradle/wrapper/gradle-wrapper.jar differ diff --git a/lang/java/gradle-plugin/gradle/wrapper/gradle-wrapper.properties b/lang/java/gradle-plugin/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..ca025c83a7c --- /dev/null +++ b/lang/java/gradle-plugin/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/lang/java/gradle-plugin/gradlew b/lang/java/gradle-plugin/gradlew new file mode 100755 index 00000000000..23d15a93670 --- /dev/null +++ b/lang/java/gradle-plugin/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original 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 +# +# https://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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/lang/java/gradle-plugin/gradlew.bat b/lang/java/gradle-plugin/gradlew.bat new file mode 100644 index 00000000000..db3a6ac207e --- /dev/null +++ b/lang/java/gradle-plugin/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lang/java/gradle-plugin/pom.xml b/lang/java/gradle-plugin/pom.xml new file mode 100644 index 00000000000..d6de00b7f48 --- /dev/null +++ b/lang/java/gradle-plugin/pom.xml @@ -0,0 +1,75 @@ + + + + 4.0.0 + + + avro-parent + org.apache.avro + 1.13.0-SNAPSHOT + ../pom.xml + + + avro-gradle-plugin + pom + + Apache Avro Gradle Plugin + Gradle plugin for Avro IDL and Specific API Compilers + + + ${project.parent.parent.basedir} + 3.3.0 + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + + + run-gradle-task + package + + exec + + + ./gradlew + + assemble + + + + + + + + com.diffplug.spotless + spotless-maven-plugin + + true + + + + + + diff --git a/lang/java/gradle-plugin/settings.gradle.kts b/lang/java/gradle-plugin/settings.gradle.kts new file mode 100644 index 00000000000..d8028b29b2e --- /dev/null +++ b/lang/java/gradle-plugin/settings.gradle.kts @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * https://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. + */ + +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" +} +rootProject.name = "avro-gradle-plugin" + + + diff --git a/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/GradlePlugin.kt b/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/GradlePlugin.kt new file mode 100644 index 00000000000..05b46415d89 --- /dev/null +++ b/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/GradlePlugin.kt @@ -0,0 +1,87 @@ +package org.apache.avro.gradle.plugin + +import org.apache.avro.gradle.plugin.extension.GradlePluginExtension +import org.apache.avro.gradle.plugin.tasks.CompileSchemaTask +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.internal.cc.base.logger +import kotlin.collections.toSet + +abstract class GradlePlugin : Plugin { + override fun apply(project: Project) { + logger.info("Running Avro Gradle plugin for project: ${project.name}") + + val extension: GradlePluginExtension = project.extensions.create("avro", GradlePluginExtension::class.java) + + // Needed for Android support + project.pluginManager.apply("java") + + project.tasks.register("avroGenerateJavaClasses", CompileSchemaTask::class.java) { compileSchemaTask -> + val sourceDirectory = extension.sourceDirectory.get() + val outputDirectory = extension.outputDirectory.get() + runPlugin(compileSchemaTask, extension, project, sourceDirectory, outputDirectory) + } + + project.tasks.register("avroGenerateTestJavaClasses", CompileSchemaTask::class.java) {compileSchemaTask -> + val sourceDirectory = extension.testSourceDirectory.get() + val outputDirectory = extension.testOutputDirectory.get() + runPlugin(compileSchemaTask, extension, project, sourceDirectory, outputDirectory) + } + } + + private fun runPlugin( + compileTask: CompileSchemaTask, + extension: GradlePluginExtension, + project: Project, + sourceDirectory: String, + outputDirectory: String + ) { + val schemaType: SchemaType = SchemaType.valueOf(extension.schemaType.get()) + + when (schemaType) { + SchemaType.schema -> { + compileTask.source(project.fileTree(sourceDirectory)) + compileTask.sourceDirectory.set(sourceDirectory) + compileTask.outputDirectory.set(outputDirectory) + compileTask.fieldVisibility.set(extension.fieldVisibility) + compileTask.setExcludes(extension.excludes.get().toSet()) + compileTask.setIncludes(setOf("**/*.avsc")) + compileTask.testExcludes.set(extension.testExcludes) + compileTask.stringType.set(extension.stringType) + compileTask.velocityToolsClassesNames.set(extension.velocityToolsClassesNames.get()) + compileTask.templateDirectory.set(extension.templateDirectory) + compileTask.recordSpecificClass.set(extension.recordSpecificClass) + compileTask.errorSpecificClass.set(extension.errorSpecificClass) + compileTask.createOptionalGetters.set(extension.createOptionalGetters) + compileTask.gettersReturnOptional.set(extension.gettersReturnOptional) + compileTask.createSetters.set(extension.createSetters) + compileTask.createNullSafeAnnotations.set(extension.createNullSafeAnnotations) + compileTask.nullSafeAnnotationNullable.set(extension.nullSafeAnnotationNullable) + compileTask.nullSafeAnnotationNotNull.set(extension.nullSafeAnnotationNotNull) + compileTask.optionalGettersForNullableFieldsOnly.set(extension.optionalGettersForNullableFieldsOnly) + compileTask.customConversions.set(extension.customConversions) + compileTask.customLogicalTypeFactories.set(extension.customLogicalTypeFactories) + compileTask.enableDecimalLogicalType.set(extension.enableDecimalLogicalType) + + addGeneratedSourcesToProject(project, compileTask.outputDirectory.get()) + } + + SchemaType.idl -> TODO() + } + } + + private fun addGeneratedSourcesToProject(project: Project, outputDirectory: String) { + val sourceSets = project.extensions.getByType(JavaPluginExtension::class.java).sourceSets + val generatedSourcesDir = project.layout.buildDirectory.dir(outputDirectory) + project.logger.debug("Generated sources directory: ${generatedSourcesDir.get()}") + + // Add directory that contains the generated Java files to source set + sourceSets.getByName("main").java.srcDir(generatedSourcesDir) + } +} + +enum class SchemaType { + schema, + idl +} diff --git a/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/extension/GradlePluginExtension.kt b/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/extension/GradlePluginExtension.kt new file mode 100644 index 00000000000..763b4e40005 --- /dev/null +++ b/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/extension/GradlePluginExtension.kt @@ -0,0 +1,226 @@ +package org.apache.avro.gradle.plugin.extension + +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import javax.inject.Inject + +abstract class GradlePluginExtension @Inject constructor(objects: ObjectFactory) { + + /** + * Schema type: "schema", "idl", "protocol" are valid. Default is "schema" + */ + val schemaType = objects.property(String::class.java).convention("schema") + + /** + * The source directory of avro files. This directory is added to the classpath + * at schema compiling time. All files can therefore be referenced as classpath + * resources following the directory structure under the source directory. + * + * @parameter property="sourceDirectory" + * default-value="${buildDirectory}/src/main/avro" + */ + val sourceDirectory: Property = objects.property(String::class.java).convention("src/main/avro") + + /** + * @parameter property="outputDirectory" + * default-value="${buildDirectory}/generated-sources/avro" + */ + val outputDirectory: Property = objects.property(String::class.java).convention("generated-sources-avro") + + + /** + * @parameter property="sourceDirectory" + * default-value="${project.layout.buildDirectory}/src/test/avro" + */ + val testSourceDirectory: Property = objects.property(String::class.java).convention("src/test/avro") + + /** + * @parameter property="outputDirectory" + * default-value="${project.layout.buildDirectory}/generated-test-sources/avro" + */ + val testOutputDirectory: Property = + objects.property(String::class.java).convention("generated-test-sources-avro") + + + /** + * The field visibility indicator for the fields of the generated class, as + * string values of SpecificCompiler.FieldVisibility. The text is case + * insensitive. + * + * @parameter default-value="PRIVATE" + */ + val fieldVisibility: Property = objects.property(String::class.java).convention("PRIVATE") + + + /** + * A set of Ant-like exclusion patterns used to prevent certain files from being + * processed. By default, this set is empty such that no files are excluded. + * + * @parameter + */ + val excludes: ListProperty = objects.listProperty(String::class.java).convention(emptyList()) + + /** + * A set of Ant-like exclusion patterns used to prevent certain files from being + * processed. By default, this set is empty such that no files are excluded. + * + * @parameter + */ + val testExcludes: ListProperty = objects.listProperty(String::class.java).convention(emptyList()) + + /** + * The Java type to use for Avro strings. May be one of CharSequence, String or + * Utf8. String by default. + * + * @parameter property="stringType" + */ + val stringType: Property = objects.property(String::class.java).convention("String") + + + /** + * The qualified names of classes which the plugin will look up, instantiate + * (through an empty constructor that must exist) and set up to be injected into + * Velocity templates by Avro compiler. + * + * @parameter property="velocityToolsClassesNames" + */ + val velocityToolsClassesNames: ListProperty = + objects.listProperty(String::class.java).convention(emptyList()) + + + /** + * The directory (within the java classpath) that contains the velocity + * templates to use for code generation. The default value points to the + * templates included with the avro-maven-plugin. + * + * @parameter property="templateDirectory" + */ + val templateDirectory: Property = + objects.property(String::class.java).convention("/org/apache/avro/compiler/specific/templates/java/classic/") + + + /** + * Generated record schema classes will extend this class. + * + * @parameter property="recordSpecificClass" + */ + val recordSpecificClass: Property = + objects.property(String::class.java).convention("org.apache.avro.specific.SpecificRecordBase") + + + /** + * Generated error schema classes will extend this class. + * + * @parameter property="errorSpecificClass" + */ + val errorSpecificClass: Property = + objects.property(String::class.java).convention("org.apache.avro.specific.SpecificExceptionBase") + + + /** + * The createOptionalGetters parameter enables generating the getOptional... + * methods that return an Optional of the requested type. This works ONLY on + * Java 8+ + * + * @parameter property="createOptionalGetters" + */ + val createOptionalGetters: Property = objects.property(Boolean::class.java).convention(false) + + /** + * The gettersReturnOptional parameter enables generating get... methods that + * return an Optional of the requested type. This works ONLY on Java 8+ + * + * @parameter property="gettersReturnOptional" + */ + val gettersReturnOptional: Property = objects.property(Boolean::class.java).convention(false) + + /** + * The optionalGettersForNullableFieldsOnly parameter works in conjunction with + * gettersReturnOptional option. If it is set, Optional getters will be + * generated only for fields that are nullable. If the field is mandatory, + * regular getter will be generated. This works ONLY on Java 8+. + * + * @parameter property="optionalGettersForNullableFieldsOnly" + */ + val optionalGettersForNullableFieldsOnly: Property = + objects.property(Boolean::class.java).convention(false) + + + /** + * Determines whether or not to create setters for the fields of the record. The + * default is to create setters. + * + * @parameter default-value="true" + */ + val createSetters: Property = objects.property(Boolean::class.java).convention(true) + + /** + * If set to true, @Nullable and @NotNull annotations are + * added to fields of the record. The default is false. If enabled, JetBrains + * annotations are used by default but other annotations can be specified via + * the nullSafeAnnotationNullable and nullSafeAnnotationNotNull parameters. + * + * @parameter property="createNullSafeAnnotations" + * + * @see [ + * JetBrains nullability annotations](https://www.jetbrains.com/help/idea/annotating-source-code.html.nullability-annotations) + */ + val createNullSafeAnnotations: Property = objects.property(Boolean::class.java).convention(false) + + /** + * Controls which annotation should be added to nullable fields if + * createNullSafeAnnotations is enabled. The default is + * org.jetbrains.annotations.Nullable. + * + * @parameter property="nullSafeAnnotationNullable" + * + * @see [ + * JetBrains nullability annotations](https://www.jetbrains.com/help/idea/annotating-source-code.html.nullability-annotations) + */ + val nullSafeAnnotationNullable: Property = + objects.property(String::class.java).convention("org.jetbrains.annotations.Nullable") + + /** + * Controls which annotation should be added to non-nullable fields if + * createNullSafeAnnotations is enabled. The default is + * org.jetbrains.annotations.NotNull. + * + * @parameter property="nullSafeAnnotationNotNull" + * + * @see [ + * JetBrains nullability annotations](https://www.jetbrains.com/help/idea/annotating-source-code.html.nullability-annotations) + */ + val nullSafeAnnotationNotNull: Property = + objects.property(String::class.java).convention("org.jetbrains.annotations.NotNull") + + /** + * A set of fully qualified class names of custom + * {@link org.apache.avro.Conversion} implementations to add to the compiler. + * The classes must be on the classpath at compile time and whenever the Java + * objects are serialized. + * + * @parameter property="customConversions" + */ + val customConversions: ListProperty = objects.listProperty(String::class.java).convention(emptyList()) + + /** + * A set of fully qualified class names of custom + * [org.apache.avro.LogicalTypes.LogicalTypeFactory] implementations to + * add to the compiler. The classes must be on the classpath at compile time and + * whenever the Java objects are serialized. + * + * @parameter property="customLogicalTypeFactories" + */ + val customLogicalTypeFactories: ListProperty = + objects.listProperty(String::class.java).convention(emptyList()) + + + /** + * Determines whether or not to use Java classes for decimal types + * + * @parameter default-value="false" + */ + val enableDecimalLogicalType: Property = objects.property(Boolean::class.java).convention(false) + +} diff --git a/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/tasks/AbstractCompileTask.kt b/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/tasks/AbstractCompileTask.kt new file mode 100644 index 00000000000..81be641e3ea --- /dev/null +++ b/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/tasks/AbstractCompileTask.kt @@ -0,0 +1,72 @@ +package org.apache.avro.gradle.plugin.tasks + +import org.gradle.api.DefaultTask +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.SourceTask + +abstract class AbstractCompileTask : SourceTask() { + + @get:Input + abstract val sourceDirectory: Property + + @get:Input + abstract val outputDirectory: Property + + @get:Input + abstract val fieldVisibility: Property + + //@get:Input + //abstract val excludes: ListProperty + + @get:Input + abstract val testExcludes: ListProperty + + @get:Input + abstract val stringType: Property + + @get:Input + abstract val velocityToolsClassesNames: ListProperty + + @get:Input + abstract val templateDirectory: Property + + @get:Input + abstract val recordSpecificClass: Property + + @get:Input + abstract val errorSpecificClass: Property + + @get:Input + abstract val createOptionalGetters: Property + + @get:Input + abstract val gettersReturnOptional: Property + + @get:Input + abstract val optionalGettersForNullableFieldsOnly: Property + + @get:Input + abstract val createSetters: Property + + @get:Input + abstract val createNullSafeAnnotations: Property + + @get:Input + abstract val nullSafeAnnotationNullable: Property + + @get:Input + abstract val nullSafeAnnotationNotNull: Property + + @get:Input + abstract val customConversions: ListProperty + + @get:Input + abstract val customLogicalTypeFactories: ListProperty + + @get:Input + abstract val enableDecimalLogicalType: Property + + +} diff --git a/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/tasks/CompileSchemaTask.kt b/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/tasks/CompileSchemaTask.kt new file mode 100644 index 00000000000..69dff11cf21 --- /dev/null +++ b/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/tasks/CompileSchemaTask.kt @@ -0,0 +1,136 @@ +package org.apache.avro.gradle.plugin.tasks + +import org.apache.avro.SchemaParseException +import org.apache.avro.SchemaParser +import org.apache.avro.compiler.specific.SpecificCompiler +import org.apache.avro.compiler.specific.SpecificCompiler.FieldVisibility +import org.apache.avro.generic.GenericData +import org.gradle.api.Project +import org.gradle.api.file.FileTree +import org.gradle.api.provider.Property +import org.gradle.api.tasks.TaskAction +import java.io.File +import java.io.IOException +import java.util.* + +abstract class CompileSchemaTask : AbstractCompileTask() { + + /** + * A set of Glob patterns used to select files from the source + * directory for processing. By default, the pattern "**/*.avsc" + * is used to select avsc files. + * + * @parameter + */ + //@get:Input + //val includes: Set = setOf("**/*.avsc") + + @TaskAction + fun compileSchema() { + project.logger.info("Generating Java files from Avro schemas...") + + if (!source.isEmpty) { + val sourceDirectoryFullPath = getSourceDirectoryFullPath(sourceDirectory) + val outputDirectoryFullPath = getBuildDirectoryFullPath(outputDirectory) + compileSchemas(source, sourceDirectoryFullPath, outputDirectoryFullPath) + } else { + logger.warn("No Avro files found in $sourceDirectory. Nothing to compile") + } + + project.logger.info("Done generating Java files from Avro schemas...") + } + + + private fun getSourceDirectoryFullPath(directoryProperty: Property): File = + project.layout.projectDirectory.dir(directoryProperty.get()).asFile + + private fun getBuildDirectoryFullPath(directoryProperty: Property): File = + project.layout.buildDirectory.dir(directoryProperty).get().asFile + + private fun compileSchemas(fileTree: FileTree, sourceDirectory: File, outputDirectory: File) { + val sourceFileForModificationDetection: File? = + fileTree + .files + .filter { file: File -> file.lastModified() > 0 } + .maxBy { it.lastModified() } + + try { + val parser = SchemaParser() + for (sourceFile in fileTree.files) { + parser.parse(sourceFile) + } + val schemas = parser.parsedNamedSchemas + + doCompile(sourceFileForModificationDetection, SpecificCompiler(schemas), outputDirectory) + } catch (ex: IOException) { + // TODO: more concrete exceptions + throw RuntimeException("IO ex: Error compiling a file in " + sourceDirectory + " to " + outputDirectory, ex) + } catch (ex: SchemaParseException) { + throw RuntimeException( + "SchemaParse ex Error compiling a file in " + sourceDirectory + " to " + outputDirectory, + ex + ) + } + } + + private fun doCompile( + sourceFileForModificationDetection: File?, + compiler: SpecificCompiler, + outputDirectory: File + ) { + setCompilerProperties(compiler) + // TODO: + // * customLogicalTypeFactories + + try { + for (customConversion in customConversions.get()) { + compiler.addCustomConversion(Thread.currentThread().getContextClassLoader().loadClass(customConversion)) + } + } catch (e: ClassNotFoundException) { + throw IOException(e) + } + compiler.compileToDestination(sourceFileForModificationDetection, outputDirectory) + } + + + private fun setCompilerProperties(compiler: SpecificCompiler) { + compiler.setTemplateDir(project.layout.projectDirectory.dir(templateDirectory.get()).asFile.absolutePath + "/") + compiler.setStringType(GenericData.StringType.valueOf(stringType.get())) + compiler.setFieldVisibility(getFieldV()) + compiler.setCreateOptionalGetters(createOptionalGetters.get()) + compiler.setGettersReturnOptional(gettersReturnOptional.get()) + compiler.setOptionalGettersForNullableFieldsOnly(optionalGettersForNullableFieldsOnly.get()) + compiler.setCreateSetters(createSetters.get()) + compiler.setCreateNullSafeAnnotations(createNullSafeAnnotations.get()) + compiler.setNullSafeAnnotationNullable(nullSafeAnnotationNullable.get()) + compiler.setNullSafeAnnotationNotNull(nullSafeAnnotationNotNull.get()) + compiler.setEnableDecimalLogicalType(enableDecimalLogicalType.get()) + // TODO: likely not needed +// compiler.setOutputCharacterEncoding(project.getProperties().getProperty("project.build.sourceEncoding")) + compiler.setAdditionalVelocityTools(instantiateAdditionalVelocityTools(velocityToolsClassesNames.get())) + compiler.setRecordSpecificClass(recordSpecificClass.get()) + compiler.setErrorSpecificClass(errorSpecificClass.get()) + } + + private fun getFieldV(): FieldVisibility { + try { + val upperCaseFieldVisibility = fieldVisibility.get().trim().uppercase(Locale.getDefault()) + return FieldVisibility.valueOf(upperCaseFieldVisibility) + } catch (e: IllegalArgumentException) { + logger.warn("Could not parse field visibility, using PRIVATE") + return FieldVisibility.PRIVATE + } + } + + protected fun instantiateAdditionalVelocityTools(velocityToolsClassesNames: List): List { + return velocityToolsClassesNames.map { velocityToolClassName -> + try { + Class.forName(velocityToolClassName) + .getDeclaredConstructor() + .newInstance() + } catch (e: Exception) { + throw RuntimeException(e) + } + } + } +} diff --git a/lang/java/gradle-plugin/src/main/resources/META-INF/org.apache.avro.gradle.plugin.properties b/lang/java/gradle-plugin/src/main/resources/META-INF/org.apache.avro.gradle.plugin.properties new file mode 100644 index 00000000000..13fbf0d2be3 --- /dev/null +++ b/lang/java/gradle-plugin/src/main/resources/META-INF/org.apache.avro.gradle.plugin.properties @@ -0,0 +1 @@ +implementation-class=org.apache.avro.gradle.plugin.GradlePlugin diff --git a/lang/java/gradle-plugin/src/test/avro/AvdlClasspathImport.avdl b/lang/java/gradle-plugin/src/test/avro/AvdlClasspathImport.avdl new file mode 100644 index 00000000000..81bdb609445 --- /dev/null +++ b/lang/java/gradle-plugin/src/test/avro/AvdlClasspathImport.avdl @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * https://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. + */ +namespace test; + +import idl "avro/User.avdl"; + +/** Ignored Doc Comment */ +/** IDL User */ +record IdlUserWrapper { + union { null, test.IdlUser } wrapped; +} diff --git a/lang/java/gradle-plugin/src/test/avro/User.avdl b/lang/java/gradle-plugin/src/test/avro/User.avdl new file mode 100644 index 00000000000..98de878d9d0 --- /dev/null +++ b/lang/java/gradle-plugin/src/test/avro/User.avdl @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * https://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. + */ +@namespace("test") +protocol IdlTest { + + enum IdlPrivacy { + Public, Private + } + + record IdlUser { + union { null, string } id; + union { null, long } createdOn; + timestamp_ms modifiedOn; + union { null, IdlPrivacy } privacy; + } + +} diff --git a/lang/java/gradle-plugin/src/test/avro/User.avpr b/lang/java/gradle-plugin/src/test/avro/User.avpr new file mode 100644 index 00000000000..6dd8b9b8900 --- /dev/null +++ b/lang/java/gradle-plugin/src/test/avro/User.avpr @@ -0,0 +1,41 @@ +{ + "protocol" : "ProtocolTest", + "namespace" : "test", + "types" : [ + { + "type" : "enum", + "name" : "ProtocolPrivacy", + "symbols" : [ "Public", "Private"] + }, + { + "type": "record", + "namespace": "test", + "name": "ProtocolUser", + "doc": "User Test Bean", + "fields": [ + { + "name": "id", + "type": ["null", "string"], + "default": null + }, + { + "name": "createdOn", + "type": ["null", "long"], + "default": null + }, + { + "name": "privacy", + "type": ["null", "ProtocolPrivacy"], + "default": null + }, + { + "name": "modifiedOn", + "type": { + "type": "long", + "logicalType": "timestamp-millis" + } + } + ] + } + ] +} diff --git a/lang/java/gradle-plugin/src/test/avro/User.avsc b/lang/java/gradle-plugin/src/test/avro/User.avsc new file mode 100644 index 00000000000..a93e0d13f21 --- /dev/null +++ b/lang/java/gradle-plugin/src/test/avro/User.avsc @@ -0,0 +1,45 @@ +{ + "type": "record", + "namespace": "test", + "name": "SchemaUser", + "doc": "User Test Bean", + "fields": [ + { + "name": "id", + "type": ["null", "string"], + "default": null + }, + { + "name": "createdOn", + "type": ["null", "long"], + "default": null + }, + { + "name": "privacy", + "type": ["null", { + "type": "enum", + "name": "SchemaPrivacy", + "namespace": "test", + "symbols" : ["Public","Private"] + }], + "default": null + }, + { + "name": "privacyImported", + "type": ["null", "test.PrivacyImport"], + "default": null + }, + { + "name": "privacyDirectImport", + "type": ["null", "test.PrivacyDirectImport"], + "default": null + }, + { + "name": "time", + "type": { + "type": "long", + "logicalType": "timestamp-millis" + } + } + ] +} diff --git a/lang/java/gradle-plugin/src/test/avro/directImport/PrivacyDirectImport.avsc b/lang/java/gradle-plugin/src/test/avro/directImport/PrivacyDirectImport.avsc new file mode 100644 index 00000000000..a5b62959206 --- /dev/null +++ b/lang/java/gradle-plugin/src/test/avro/directImport/PrivacyDirectImport.avsc @@ -0,0 +1,7 @@ +{ + "type": "enum", + "namespace": "test", + "name": "PrivacyDirectImport", + "doc": "Privacy Test Enum", + "symbols" : ["Public","Private"] +} diff --git a/lang/java/gradle-plugin/src/test/avro/extends/Custom.avsc b/lang/java/gradle-plugin/src/test/avro/extends/Custom.avsc new file mode 100644 index 00000000000..63056e5d17f --- /dev/null +++ b/lang/java/gradle-plugin/src/test/avro/extends/Custom.avsc @@ -0,0 +1,18 @@ +{ + "type": "record", + "namespace": "test", + "name": "SchemaCustom", + "doc": "Custom Test Bean", + "fields": [ + { + "name": "id", + "type": ["null", "string"], + "default": null + }, + { + "name": "createdOn", + "type": ["null", "long"], + "default": null + } + ] +} diff --git a/lang/java/gradle-plugin/src/test/avro/imports/PrivacyImport.avsc b/lang/java/gradle-plugin/src/test/avro/imports/PrivacyImport.avsc new file mode 100644 index 00000000000..f454f1d3996 --- /dev/null +++ b/lang/java/gradle-plugin/src/test/avro/imports/PrivacyImport.avsc @@ -0,0 +1,7 @@ +{ + "type": "enum", + "namespace": "test", + "name": "PrivacyImport", + "doc": "Privacy Test Enum", + "symbols" : ["Public","Private"] +} diff --git a/lang/java/gradle-plugin/src/test/avro/multipleSchemas/ApplicationEvent.avsc b/lang/java/gradle-plugin/src/test/avro/multipleSchemas/ApplicationEvent.avsc new file mode 100644 index 00000000000..efc7fbf6139 --- /dev/null +++ b/lang/java/gradle-plugin/src/test/avro/multipleSchemas/ApplicationEvent.avsc @@ -0,0 +1,44 @@ +{ + "namespace": "model", + "type": "record", + "doc": "", + "name": "ApplicationEvent", + "fields": [ + { + "name": "applicationId", + "type": "string", + "doc": "Application ID" + }, + { + "name": "status", + "type": "string", + "doc": "Application Status" + }, + { + "name": "documents", + "type": ["null", { + "type": "array", + "items": "model.DocumentInfo" + }], + "doc": "", + "default": null + }, + { + "name": "response", + "type": { + "namespace": "model", + "type": "record", + "doc": "", + "name": "MyResponse", + "fields": [ + { + "name": "isSuccessful", + "type": "boolean", + "doc": "Indicator for successful or unsuccessful call" + } + ] + } + } + ] + +} diff --git a/lang/java/gradle-plugin/src/test/avro/multipleSchemas/DocumentInfo.avsc b/lang/java/gradle-plugin/src/test/avro/multipleSchemas/DocumentInfo.avsc new file mode 100644 index 00000000000..95dd4243ea6 --- /dev/null +++ b/lang/java/gradle-plugin/src/test/avro/multipleSchemas/DocumentInfo.avsc @@ -0,0 +1,19 @@ +{ + "namespace": "model", + "type": "record", + "doc": "", + "name": "DocumentInfo", + "fields": [ + { + "name": "documentId", + "type": "string", + "doc": "Document ID" + }, + { + "name": "filePath", + "type": "string", + "doc": "Document Path" + } + ] + +} diff --git a/lang/java/gradle-plugin/src/test/avro/multipleSchemas/MyResponse.avsc b/lang/java/gradle-plugin/src/test/avro/multipleSchemas/MyResponse.avsc new file mode 100644 index 00000000000..ac6d08291d9 --- /dev/null +++ b/lang/java/gradle-plugin/src/test/avro/multipleSchemas/MyResponse.avsc @@ -0,0 +1,14 @@ +{ + "namespace": "model", + "type": "record", + "doc": "", + "name": "MyResponse", + "fields": [ + { + "name": "isSuccessful", + "type": "boolean", + "doc": "Indicator for successful or unsuccessful call" + } + ] + +} diff --git a/lang/java/gradle-plugin/src/test/avro/multipleSchemas/README.md b/lang/java/gradle-plugin/src/test/avro/multipleSchemas/README.md new file mode 100644 index 00000000000..fe3541b660e --- /dev/null +++ b/lang/java/gradle-plugin/src/test/avro/multipleSchemas/README.md @@ -0,0 +1,8 @@ +## test for parsing multiple files. +This folder aims to test `public List Schema.parse(Iterable sources) throws IOException` method. + +The objective is to check that a record schema define in a file can be use in another record schema as a field type. +Here, ApplicationEvent.avsc file contains a field of type DocumentInfo, defined in file DocumentInfo.avsc. + +The is written at TestSchema.testParseMultipleFile. + diff --git a/lang/java/gradle-plugin/src/test/kotlin/org/apache/avro/gradle/plugin/SchemaCompileTaskTest.kt b/lang/java/gradle-plugin/src/test/kotlin/org/apache/avro/gradle/plugin/SchemaCompileTaskTest.kt new file mode 100644 index 00000000000..7da2008d75e --- /dev/null +++ b/lang/java/gradle-plugin/src/test/kotlin/org/apache/avro/gradle/plugin/SchemaCompileTaskTest.kt @@ -0,0 +1,271 @@ +package org.apache.avro.gradle.plugin + +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.junit.jupiter.api.io.TempDir +import java.nio.file.Path +import kotlin.io.path.* +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +@ExperimentalPathApi +class SchemaCompileTaskTest { + + @TempDir + lateinit var tempDir: Path + + @Test + fun `plugin executes avroGenerateJavaClasses task successfully`() { + // given + val tempSettingsFile = tempDir.resolve("settings.gradle.kts") + val tempBuildFile = tempDir.resolve("build.gradle.kts") + val tempAvroSrcDir = tempDir.resolve("src/test/avro").createDirectories() + + val testAvroFiles = Path.of("src/test/avro") + val testAvroOutPutDir = Path.of("generated-sources/avro") + + val testOutPutDirectory = tempDir.resolve("build/$testAvroOutPutDir/test") + + testAvroFiles.copyToRecursively( + tempAvroSrcDir, + overwrite = true, + followLinks = false + ) + + tempSettingsFile.writeText("") + tempBuildFile.writeText( + """ + plugins { + id("org.apache.avro.avro-gradle-plugin") + } + + avro { + schemaType = "schema" + sourceDirectory = "$testAvroFiles" + outputDirectory = "$testAvroOutPutDir" + } + """.trimIndent() + ) + + // when + val result = GradleRunner.create() + .withProjectDir(tempDir.toFile()) + .withArguments("avroGenerateJavaClasses") + .withPluginClasspath() + .forwardOutput() // to see printLn in code + .build() + + val expectedFiles = setOf( + "SchemaPrivacy.java", + "SchemaUser.java", + "PrivacyImport.java", + "SchemaCustom.java", + "PrivacyDirectImport.java" + ) + + // then + assertEquals(TaskOutcome.SUCCESS, result.task(":avroGenerateJavaClasses")?.outcome) + assertFilesExist(testOutPutDirectory, expectedFiles) + + val schemaUserContent = testOutPutDirectory.resolve("SchemaUser.java").readText() + assertTrue(schemaUserContent.contains("java.time.Instant")) + } + + + @Test + fun `plugin executes avroGenerateTestJavaClasses task successfully - for files in test directory`() { + // given + val tempSettingsFile = tempDir.resolve("settings.gradle.kts") + val tempBuildFile = tempDir.resolve("build.gradle.kts") + val tempAvroSrcDir = tempDir.resolve("src/test/avro").createDirectories() + + val testAvroFiles = Path.of("src/test/avro") + val testAvroOutPutDir = Path.of("generated-test-sources-avro") + + val testOutPutDirectory = tempDir.resolve("build/$testAvroOutPutDir/test") + + testAvroFiles.copyToRecursively( + tempAvroSrcDir, + overwrite = true, + followLinks = false + ) + + tempSettingsFile.writeText("") + tempBuildFile.writeText( + """ + plugins { + id("org.apache.avro.avro-gradle-plugin") + } + + avro { + schemaType = "schema" + testSourceDirectory = "$testAvroFiles" + testOutputDirectory = "$testAvroOutPutDir" + } + """.trimIndent() + ) + + // when + val result = GradleRunner.create() + .withProjectDir(tempDir.toFile()) + .withArguments("avroGenerateTestJavaClasses") + .withPluginClasspath() + .forwardOutput() // to see printLn in code + .build() + + val expectedFiles = setOf( + "SchemaPrivacy.java", + "SchemaUser.java", + "PrivacyImport.java", + "SchemaCustom.java", + "PrivacyDirectImport.java" + ) + + // then + assertEquals(TaskOutcome.SUCCESS, result.task(":avroGenerateTestJavaClasses")?.outcome) + assertFilesExist(testOutPutDirectory, expectedFiles) + + val schemaUserContent = testOutPutDirectory.resolve("SchemaUser.java").readText() + assertTrue(schemaUserContent.contains("java.time.Instant")) + } + + @Test + fun `plugin executes avroGenerateJavaClasses task successfully - with Velocity class names`() { + // given + val tempSettingsFile = tempDir.resolve("settings.gradle.kts") + val tempBuildFile = tempDir.resolve("build.gradle.kts") + val tempAvroSrcDir = tempDir.resolve("src/test/avro").createDirectories() + //val bla = tempDir.resolve("src/aap").createDirectories() + val tempVelocityToolClassesDir = tempDir.resolve("src/test/resources/templates").createDirectories() + + val testAvroFilesDir = Path.of("src/test/avro") + val testAvroOutPutDir = Path.of("generated-sources-avro") + val testVelocityToolClassesDir = Path.of("src/test/resources/templates") + + val testOutPutDirectory = tempDir.resolve("build/$testAvroOutPutDir/test") + + testAvroFilesDir.copyToRecursively( + tempAvroSrcDir, + overwrite = true, + followLinks = false + ) + + testVelocityToolClassesDir.copyToRecursively( + tempVelocityToolClassesDir, + overwrite = true, + followLinks = false + ) + + tempSettingsFile.writeText("") + tempBuildFile.writeText( + """ + plugins { + id("org.apache.avro.avro-gradle-plugin") + } + + avro { + schemaType = "schema" + sourceDirectory = "$testAvroFilesDir" + outputDirectory = "$testAvroOutPutDir" + templateDirectory = "$testVelocityToolClassesDir" + velocityToolsClassesNames = listOf("java.lang.String") + } + """.trimIndent() + ) + + // when + val result = GradleRunner.create() + .withProjectDir(tempDir.toFile()) + .withArguments("avroGenerateJavaClasses") + .withPluginClasspath() + .forwardOutput() // to see printLn in code + .build() + + val expectedFiles = setOf( + "SchemaPrivacy.java", + "SchemaUser.java", + "PrivacyImport.java", + "SchemaCustom.java", + "PrivacyDirectImport.java" + ) + + // then + assertEquals(TaskOutcome.SUCCESS, result.task(":avroGenerateJavaClasses")?.outcome) + assertFilesExist(testOutPutDirectory, expectedFiles) + + val schemaUserContent = testOutPutDirectory.resolve("SchemaUser.java").readText() + assertTrue(schemaUserContent.contains("It works!")) + } + + @Test + fun `plugin executes avroGenerateJavaClasses task successfully - custom recordSpecificClass`() { + // given + val tempSettingsFile = tempDir.resolve("settings.gradle.kts") + val tempBuildFile = tempDir.resolve("build.gradle.kts") + val tempAvroSrcDir = tempDir.resolve("src/test/avro").createDirectories() + + val testAvroFiles = Path.of("src/test/avro") + val testAvroOutPutDir = Path.of("generated-sources/avro") + + val testOutPutDirectory = tempDir.resolve("build/$testAvroOutPutDir/test") + + testAvroFiles.copyToRecursively( + tempAvroSrcDir, + overwrite = true, + followLinks = false + ) + + tempSettingsFile.writeText("") + tempBuildFile.writeText( + """ + plugins { + id("org.apache.avro.avro-gradle-plugin") + } + + avro { + schemaType = "schema" + sourceDirectory = "$testAvroFiles" + outputDirectory = "$testAvroOutPutDir" + recordSpecificClass = "org.apache.avro.custom.CustomRecordBase" + } + """.trimIndent() + ) + + // when + val result = GradleRunner.create() + .withProjectDir(tempDir.toFile()) + .withArguments("avroGenerateJavaClasses") + .withPluginClasspath() + .forwardOutput() // to see printLn in code + .build() + + // then + assertEquals(TaskOutcome.SUCCESS, result.task(":avroGenerateJavaClasses")?.outcome) + + val outPutFile = testOutPutDirectory.resolve("SchemaCustom.java") + assertTrue(outPutFile.toFile().exists()) + + val extendsLines = outPutFile.readLines() + .filter { line -> line.contains("class SchemaCustom extends ") } + assertEquals(1, extendsLines.size) + + val extendLine = extendsLines[0] + assertTrue(extendLine.contains(" org.apache.avro.custom.CustomRecordBase ")) + assertFalse(extendLine.contains("org.apache.avro.specific.SpecificRecordBase")) + } + + + private fun assertFilesExist(directory: Path, expectedFiles: Set) { + assertTrue(directory.exists(), "Directory $directory does not exist") + assertTrue(expectedFiles.isNotEmpty()) + + val filesInDirectory: Set = directory + .listDirectoryEntries() + .map { it.fileName.toString() }.toSet() + + assertEquals(expectedFiles, filesInDirectory) + } + +} diff --git a/lang/java/gradle-plugin/src/test/resources/templates/enum.vm b/lang/java/gradle-plugin/src/test/resources/templates/enum.vm new file mode 100644 index 00000000000..fbb32a9583a --- /dev/null +++ b/lang/java/gradle-plugin/src/test/resources/templates/enum.vm @@ -0,0 +1,19 @@ +## +## Licensed to the Apache Software Foundation (ASF) under one +## or more contributor license agreements. See the NOTICE file +## distributed with this work for additional information +## regarding copyright ownership. The ASF licenses this file +## to you 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 +## +## https://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. +## + +## No need to place anything here by now. File must exist, though. diff --git a/lang/java/gradle-plugin/src/test/resources/templates/protocol.vm b/lang/java/gradle-plugin/src/test/resources/templates/protocol.vm new file mode 100644 index 00000000000..fbb32a9583a --- /dev/null +++ b/lang/java/gradle-plugin/src/test/resources/templates/protocol.vm @@ -0,0 +1,19 @@ +## +## Licensed to the Apache Software Foundation (ASF) under one +## or more contributor license agreements. See the NOTICE file +## distributed with this work for additional information +## regarding copyright ownership. The ASF licenses this file +## to you 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 +## +## https://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. +## + +## No need to place anything here by now. File must exist, though. diff --git a/lang/java/gradle-plugin/src/test/resources/templates/record.vm b/lang/java/gradle-plugin/src/test/resources/templates/record.vm new file mode 100644 index 00000000000..859bc062968 --- /dev/null +++ b/lang/java/gradle-plugin/src/test/resources/templates/record.vm @@ -0,0 +1,21 @@ +## +## Licensed to the Apache Software Foundation (ASF) under one +## or more contributor license agreements. See the NOTICE file +## distributed with this work for additional information +## regarding copyright ownership. The ASF licenses this file +## to you 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 +## +## https://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. +## + +/** + * $string.concat("It works!") + */ diff --git a/lang/java/pom.xml b/lang/java/pom.xml index 3e503842bbc..7cbf4870bf7 100644 --- a/lang/java/pom.xml +++ b/lang/java/pom.xml @@ -334,6 +334,7 @@ com.diffplug.spotless spotless-maven-plugin + true CHANGES.txt DIST_README.txt