-
Notifications
You must be signed in to change notification settings - Fork 3
/
libs.js
191 lines (164 loc) · 5.03 KB
/
libs.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
'use strict'
const paths = require('path')
const buck = require('./buck')
const ops = require('./ops')
const mvn = require('./mvn')
const lock = require('./lock')
// TODO establish a way to declare non-exported dependencies for libraries
// currently each such library have jars as exported dependencies, need implement
// configuration syntax to mark some jars non-exported and make sure this works in IDE
const GEN_BANNER = '# Generated by up.js --lib, do not edit, manual edits will be overridden'
class Lib {
constructor(target, jars, srcs, options) {
Object.assign(this, {
target,
jars,
srcs,
options,
})
}
get path() {
return this.target.path
}
get name() {
return `//${this.target.abbr}`
}
get flatname() {
return this.name.replace(/[-.:/]/g, '_')
}
symlinkJar(jar) {
return paths.join(this.path, '.out', `${jar.filenameJar}`)
}
symlinkSrc(jar) {
return paths.join(this.path, '.out', `${jar.filenameSrc}`)
}
toBuckRules() {
return buck.rules(this.target, this.jars, this.srcs, this.options)
}
toString() {
return `${this.name} [${this.jars.join(' ')}]`
}
}
Lib.fromRaw = function(target, jars, options) {
if (!options && jars.constructor == Object) {
// shortened syntax when no maven jars, only options
options = jars
jars = []
}
options = options || {}
target = buck.target(target)
if (options.internal) {
let filenameJar = options.jar || (target.goal + '.jar'),
filenameSrc = options.src || (target.goal + '.src.zip')
// '.src.zip' learned that src works best with zip extension, not jar
// so that they can be included into 'srcs' of java_library
let asJars = [{
filenameJar,
filenameSrc,
toString() { return `internal: ${filenameJar}, ${filenameSrc}` }
}]
return new Lib(target, asJars, asJars, options)
}
let toCoords = j => mvn.coords(j, options)
jars = [].concat(jars).map(toCoords)
let srcs = options.srcs && [].concat(options.srcs).map(toCoords)
if (srcs
&& srcs.length > 0
&& srcs.length !== jars.length) {
throw `Library ${target}, 'options.srcs' are not matching number or jars`
}
return new Lib(target, jars, srcs || jars, options)
}
module.exports = {
includes: [],
staged: [],
all: [],
byTarget: {},
byPath: {},
toString() {
return ['Libraries', ...this.all].map(String).join('\n\t')
},
include(loader, options) {
// loader should be parameterless callback to
// call require from the context of the caller
// or anything which supplies function that called
// and it replays .lib directives
// loader: ()=>(libs, options)=>void
// where
// libs: {lib: ()=>Self}
// options: {repo}
this.includes.push({loader, options})
},
prepare() {
if (this.all.length) return
lock.load()
.map(([...args]) => Lib.fromRaw(...args))
.forEach(l => this.add(l))
},
uplock() {
if (this.all.length) {
ops.err('intenal problem: libraries already defined')
return
}
if (lock.exists()) {
// Load from lockfile to cache known checksums,
// however we are not applying libraries from lockfile
// by discarding the result
lock.load()
}
// here we process delayed includes, because those includes may
// not be available yet as files when commands like `up --grab`
// is executed to actually download these
// please take a note, that we only allow `.lib()` directives in included
// library scripts, not the full set directives on `up` object
// we do look at includes when regenerating libraries/lock file
// and ignore those includes when just redoing libraries from lock etc
for (let {loader, options} of this.includes) {
let self = this
loader()({
lib(...args) {
self.stage(...args)
return this
}
}, options)
}
this.includes = []
this.staged.forEach(l => this.add(l))
lock.store(this.staged)
},
stage(...args) {
this.staged.push(Lib.fromRaw(...args))
},
add(lib) {
this.all.push(lib)
this.addByTarget(lib)
this.addByPath(lib)
},
addByTarget(lib) {
let k = String(lib.target)
if (k in this.byTarget) throw `Duplicate library ${k}`
this.byTarget[k] = lib
},
addByPath(lib) {
let d = this.byPath
;(d[lib.path] || (d[lib.path] = [])).push(lib)
},
genBuckfiles() {
for (let [path, ls] of Object.entries(this.byPath)) {
if (ls.some(l => l.options.internal)) {
if (ls.some(l => !l.options.internal)) {
throw `Don't mix internal libraries with 3rd-party on a same path ${path}`
}
// don't write internal libraries they already exist, we only need
// to make sure they are properly propagated in IDE libraries
} else {
ops.write(
paths.join(path, 'BUCK'),
[GEN_BANNER, ...ls.flatMap(l => l.toBuckRules())].join(''))
}
}
// we've written some BUCK files so make sure we
// will re-query buck
buck.dropCache()
},
}