| 219 | } |
| 220 | |
| 221 | async compileLocalFile( |
| 222 | type: 'interactor' | 'validator' | 'checker' | 'generator' | 'manager' | 'std', |
| 223 | source: CompilableSource, checkerType?: string, |
| 224 | ): Promise<Execute> { |
| 225 | if (type === 'checker' && ['default', 'strict'].includes(checkerType)) { |
| 226 | return { |
| 227 | execute: '', |
| 228 | copyIn: {}, |
| 229 | clean: () => Promise.resolve(null), |
| 230 | [Symbol.asyncDispose]: () => Promise.resolve(null), |
| 231 | }; |
| 232 | } |
| 233 | if (type === 'checker' && !checkers[checkerType]) throw new FormatError('Unknown checker type {0}.', [checkerType]); |
| 234 | if (this.compileCache?.[type]) { |
| 235 | return { |
| 236 | ...this.compileCache[type], |
| 237 | clean: () => Promise.resolve(null), |
| 238 | }; |
| 239 | } |
| 240 | using span = this.startChildSpan('judge.compileLocalFile', { type, checkerType: checkerType || '', cached: false }); |
| 241 | const withTestlib = type !== 'std' && (type !== 'checker' || checkerType === 'testlib'); |
| 242 | const extra = type === 'std' ? this.config.user_extra_files : this.config.judge_extra_files; |
| 243 | const copyIn = { |
| 244 | ...Object.fromEntries( |
| 245 | (extra || []).map((i) => [basename(i), { src: i }]), |
| 246 | ), |
| 247 | ...(withTestlib ? { 'testlib.h': testlibFile } : {}), |
| 248 | } as CopyIn; |
| 249 | let [file, langId] = typeof source === 'string' ? [source, 'auto'] : [source.file, source.lang]; |
| 250 | if (!file.startsWith('/')) file = join(this.folder, file); |
| 251 | let lang; |
| 252 | if (langId === 'auto') { |
| 253 | const s = file.replace('@', '.').split('.'); |
| 254 | langId = s.pop(); |
| 255 | while (s.length) { |
| 256 | lang = this.session.getLang(langId, false); |
| 257 | if (lang) break; |
| 258 | langId = `${s.pop()}.${langId}`; |
| 259 | } |
| 260 | } else lang = this.session.getLang(langId, false); |
| 261 | if (!lang) throw new FormatError(`Unknown ${type} language: ${langId}.`); |
| 262 | span.setAttribute('lang', langId); |
| 263 | // TODO cache compiled binary |
| 264 | const result = await compile(lang, { src: file }, copyIn); |
| 265 | if (!result._cacheable) { |
| 266 | await this.pushClean(result.clean); |
| 267 | return result; |
| 268 | } |
| 269 | span.setAttribute('cacheable', true); |
| 270 | await Lock.acquire(this.folder); |
| 271 | try { |
| 272 | const loc = join(this.folder, `_${type}.cache`); |
| 273 | const newCopyIn = { ...result.copyIn, [result._cacheable]: { src: loc } }; |
| 274 | // compiled checker should no longer need header file |
| 275 | // delete this copyIn as it's shipped with hydrojudge and may disappear after upgrade |
| 276 | delete newCopyIn['testlib.h']; |
| 277 | this.compileCache[type] = { |
| 278 | execute: result.execute, |