1import * as rust from './rust';
2import path from 'path';
3import Crate from './crate';
4import BuildSettings from './build-settings';
5import { rimraf } from './async/rimraf';
6
7const LIB_PREFIX: Record<string, string> = {
8  'darwin':  "lib",
9  'freebsd': "lib",
10  'linux':   "lib",
11  'sunos':   "lib",
12  'win32':   ""
13};
14
15const LIB_SUFFIX: Record<string, string> = {
16  'darwin':  ".dylib",
17  'freebsd': ".so",
18  'linux':   ".so",
19  'sunos':   ".so",
20  'win32':   ".dll"
21};
22
23export type TargetOptions = {
24  release?: boolean,
25  arch?: string
26};
27
28/** The Rust build artifacts for a single build target of a Neon crate. */
29export default class Target {
30  readonly crate: Crate;
31  readonly release: boolean;
32  readonly arch: string;
33  readonly triple: string;
34  readonly subdirectory: string;
35  readonly root: string;
36  readonly dylib: string;
37
38  constructor(crate: Crate, options: TargetOptions = {}) {
39    let { release = true, arch = process.env.npm_config_arch || process.arch } = options;
40    this.crate = crate;
41    this.release = release;
42    this.arch = arch;
43
44    if (process.platform === 'win32') {
45      this.triple = (arch === 'ia32') ? 'i686-pc-windows-msvc' : 'x86_64-pc-windows-msvc';
46    } else {
47      this.triple = '';
48    }
49
50    if (process.env.CARGO_BUILD_TARGET) {
51      this.triple = process.env.CARGO_BUILD_TARGET;
52    }
53
54    this.subdirectory = path.join(this.triple, release ? 'release' : 'debug');
55    this.root = path.resolve(crate.project.targetDirectory, this.subdirectory);
56
57    let prefix = LIB_PREFIX[process.platform];
58    let suffix = LIB_SUFFIX[process.platform];
59    this.dylib = path.resolve(this.root, prefix + crate.name + suffix);
60  }
61
62  async clean() {
63    // Remove the directory associated with this target.
64    const absolutePathSubdir = path.resolve(this.crate.root, 'target', this.subdirectory);
65    await rimraf(absolutePathSubdir);
66
67    // If this target was the active target, remove the addon.
68    if (this.crate.artifacts.haveActivated(this.subdirectory)) {
69      await this.crate.removeAddon();
70    }
71
72    // Update the build state.
73    this.crate.artifacts.delete(this.subdirectory);
74    this.crate.saveArtifacts();
75  }
76
77  async build(toolchain: rust.Toolchain,
78              settings: BuildSettings,
79              additionalArgs: string[])
80  {
81    let releaseFlags = this.release ? ["--release"] : [];
82    let targetFlags = this.triple ? ["--target=" + this.triple] : [];
83
84    let args = ['build'].concat(releaseFlags, targetFlags, additionalArgs);
85
86    try {
87      let result = await rust.spawn("cargo", args, toolchain, {
88        cwd: this.crate.root,
89        stdio: 'inherit'
90      });
91
92      if (result !== 0) {
93        throw new Error("cargo build failed");
94      }
95
96      this.crate.artifacts.activate(this.subdirectory, settings);
97
98      return result;
99    } finally {
100      this.crate.saveArtifacts();
101    }
102  }
103
104  inState(settings: BuildSettings) {
105    let savedSettings = this.crate.artifacts.lookup(this.subdirectory);
106    return savedSettings && savedSettings.match(settings);
107  }
108
109};
110