<commit_before>use autograd::{Function, ExecutionEngine};
use tensor::Tensor;
use std::ops::{AddAssign, Index};
use tensor::*;
use ::*;

pub type VarList<T> = Vec<Variable<T>>;

pub enum SavedVarKind {
    FloatVariable(SavedVariable<f32>),
    LongVariable(SavedVariable<i64>),
}
pub enum VarKind {
    FloatVariable(Variable<f32>),
    LongVariable(Variable<i64>),
}

impl Clone for VarKind {
    fn clone(&self) -> Self {
        unimplemented!()
    }
}

pub struct VariableImpl<T> {
    // AKA Creator
    grad_fn: OptRcMut<Function>,
    grad: OptRcMut<Variable<T>>,
    // version_counter etc ...
    dirty: bool,
    volatile: bool,
    requires_grad: bool,
}

impl<T> VariableImpl<T> {
    fn new(args: VariableArgs) -> Self {

        let grad_fn = match args.creator {
            Some(creator) => Some(RcMutNew(creator)),
            _ => None,
        };

        VariableImpl {
            grad_fn: grad_fn,
            grad: None,
            dirty: false,
            volatile: args.volatile,
            requires_grad: args.requires_grad,
        }
    }
}

pub struct Variable<T> {
    pub data: Tensor<T>,
    value: RcMut<VariableImpl<T>>,
}

impl<T> Clone for Variable<T> {
    fn clone(&self) -> Self {
        Variable {
            data: self.data.clone(),
            value: self.value.clone(),
        }
    }
}

pub struct SavedVariable<T> {
    data: Box<Tensor<T>>,
    grad_fn: OptRcMut<Function>,
    grad: OptRcMut<Variable<T>>,
	// version_counter etc ...
}

#[derive(Default, Clone)]
pub struct BackwardArgs {}

pub struct VariableArgs {
    pub creator: Option<Function>,
    pub volatile: bool,
    pub requires_grad: bool,
}

impl Default for VariableArgs {
    fn default() -> Self {
        VariableArgs {
            creator: None,
            volatile: false,
            requires_grad: true,
        }
    }
}

impl<T> Variable<T> {
    pub fn new(data: Tensor<T>) -> Self {
        Variable::new_args(data, VariableArgs::default())
    }
    fn inner(&mut self) -> RefMut<VariableImpl<T>> {
        self.value.borrow_mut()
    }
    pub fn is_volatile(&self) -> bool {
        self.inner().volatile
    }
    pub fn new_args(data: Tensor<T>, args: VariableArgs) -> Self {
        Variable {
            data: data,
            value: RcMutNew(VariableImpl::new(args)),
        }
    }
    pub fn apply(&mut self, callback: fn(&mut Tensor<T>)) {
        callback(&mut self.data);
    }
    pub fn mark_dirty(&mut self) {
        self.value.borrow_mut().dirty = true;
    }
    pub fn view(&self, dims: &[i32]) -> Self {
        unimplemented!()
    }
    // Computes the gradient of current variable w.r.t. graph leaves
    pub fn backward_args(&mut self, gradient_: Option<&mut Tensor<T>>, retain_variables: bool) {
        let mut store;
        if self.inner().volatile {
            panic!("calling backward on a volatile variable")
        }
        if !self.inner().requires_grad {
            panic!("calling backward on a variable that doesn't require a gradient")
        }
        let mut gradient = match gradient_ {
            Some(gradient) => gradient,
            None => {
                store = self.data.new_(1);
                &mut store
            }
        };
        ExecutionEngine::run_backward(self, &mut gradient, retain_variables)
    }
    pub fn backward(&mut self) {
        self.backward_args(None, false)
    }
    // Detach from graph
    pub fn detach_(&mut self) {
        unimplemented!()
    }
    // return a new variable detached from graph
    pub fn detach(&self) -> Variable<T> {
        unimplemented!()
    }
}

impl<T> Default for Variable<T> {
    fn default() -> Self {
        unimplemented!()
    }
}

impl<T: Copy> Index<isize> for Variable<T> {
    type Output = T;

    fn index(&self, idx: isize) -> &Self::Output {
        unimplemented!()
    }
}

impl AddAssign<Variable<f32>> for f32 {
    fn add_assign(&mut self, rhs: Variable<f32>) {
        *self = *self + rhs[0]
    }
}
<commit_msg>fix compile<commit_after>use autograd::{Function, ExecutionEngine};
use tensor::Tensor;
use std::ops::{AddAssign, Index};
use tensor::*;
use ::*;

pub type VarList<T> = Vec<Variable<T>>;

pub enum SavedVarKind {
    FloatVariable(SavedVariable<f32>),
    LongVariable(SavedVariable<i64>),
}
pub enum VarKind {
    FloatVariable(Variable<f32>),
    LongVariable(Variable<i64>),
}

impl Clone for VarKind {
    fn clone(&self) -> Self {
        unimplemented!()
    }
}

pub struct VariableImpl<T> {
    // AKA Creator
    grad_fn: OptRcMut<Function>,
    grad: OptRcMut<Variable<T>>,
    // version_counter etc ...
    dirty: bool,
    volatile: bool,
    requires_grad: bool,
}

impl<T> VariableImpl<T> {
    fn new(args: VariableArgs) -> Self {

        let grad_fn = match args.creator {
            Some(creator) => Some(RcMutNew(creator)),
            _ => None,
        };

        VariableImpl {
            grad_fn: grad_fn,
            grad: None,
            dirty: false,
            volatile: args.volatile,
            requires_grad: args.requires_grad,
        }
    }
}

pub struct Variable<T> {
    pub data: Tensor<T>,
    value: RcMut<VariableImpl<T>>,
}

impl<T> Clone for Variable<T> {
    fn clone(&self) -> Self {
        Variable {
            data: self.data.clone(),
            value: self.value.clone(),
        }
    }
}

pub struct SavedVariable<T> {
    data: Box<Tensor<T>>,
    grad_fn: OptRcMut<Function>,
    grad: OptRcMut<Variable<T>>,
	// version_counter etc ...
}

#[derive(Default, Clone)]
pub struct BackwardArgs {}

pub struct VariableArgs {
    pub creator: Option<Function>,
    pub volatile: bool,
    pub requires_grad: bool,
}

impl Default for VariableArgs {
    fn default() -> Self {
        VariableArgs {
            creator: None,
            volatile: false,
            requires_grad: true,
        }
    }
}

impl<T> Variable<T> {
    pub fn new(data: Tensor<T>) -> Self {
        Variable::new_args(data, VariableArgs::default())
    }
    fn inner(&mut self) -> RefMut<VariableImpl<T>> {
        self.value.borrow_mut()
    }
    pub fn is_volatile(&self) -> bool {
        self.value.borrow().volatile
    }
    pub fn new_args(data: Tensor<T>, args: VariableArgs) -> Self {
        Variable {
            data: data,
            value: RcMutNew(VariableImpl::new(args)),
        }
    }
    pub fn apply(&mut self, callback: fn(&mut Tensor<T>)) {
        callback(&mut self.data);
    }
    pub fn mark_dirty(&mut self) {
        self.value.borrow_mut().dirty = true;
    }
    pub fn view(&self, dims: &[i32]) -> Self {
        unimplemented!()
    }
    // Computes the gradient of current variable w.r.t. graph leaves
    pub fn backward_args(&mut self, gradient_: Option<&mut Tensor<T>>, retain_variables: bool) {
        let mut store;
        if self.inner().volatile {
            panic!("calling backward on a volatile variable")
        }
        if !self.inner().requires_grad {
            panic!("calling backward on a variable that doesn't require a gradient")
        }
        let mut gradient = match gradient_ {
            Some(gradient) => gradient,
            None => {
                store = self.data.new_(1);
                &mut store
            }
        };
        ExecutionEngine::run_backward(self, &mut gradient, retain_variables)
    }
    pub fn backward(&mut self) {
        self.backward_args(None, false)
    }
    // Detach from graph
    pub fn detach_(&mut self) {
        unimplemented!()
    }
    // return a new variable detached from graph
    pub fn detach(&self) -> Variable<T> {
        unimplemented!()
    }
}

impl<T> Default for Variable<T> {
    fn default() -> Self {
        unimplemented!()
    }
}

impl<T: Copy> Index<isize> for Variable<T> {
    type Output = T;

    fn index(&self, idx: isize) -> &Self::Output {
        unimplemented!()
    }
}

impl AddAssign<Variable<f32>> for f32 {
    fn add_assign(&mut self, rhs: Variable<f32>) {
        *self = *self + rhs[0]
    }
}
<|endoftext|>


<commit_before>pub mod segment;
pub mod agent;
pub mod swarm;
pub mod gen;
pub mod phen;

use backend::obj;
use backend::obj::*;
use rand;
use chrono::*;
use std::f32::consts;
use std::collections::HashMap;
use std::collections::HashSet;
use std::io;
use std::io::Write;
use std::fs;

use core::geometry::*;
use core::resource::ResourceLoader;
use backend::world::agent::Agent;
use backend::world::agent::AgentType;
use backend::world::agent::TypedAgent;
use backend::world::swarm::*;
use serialize::base64::{self, ToBase64};

pub struct World {
	pub extent: Rect,
	swarms: HashMap<AgentType, Swarm>,
	emitters: Vec<Emitter>,
	registered: HashSet<Id>,
	minion_gene_pool: gen::GenePool,
	resource_gene_pool: gen::GenePool,
}

pub trait WorldState {
	fn agent(&self, id: obj::Id) -> Option<&Agent>;
}

impl WorldState for World {
	fn agent(&self, id: obj::Id) -> Option<&Agent> {
		self.swarms.get(&id.type_of()).and_then(|m| m.get(id))
	}
}

#[derive(Clone)]
pub struct Emitter {
	transform: Transform,
	rate: f32,
}

impl Emitter {
	pub fn new(x: f32, y: f32, rate: f32) -> Self {
		Emitter {
			transform: Transform::from_position(Position::new(x, y)),
			rate: rate,
		}
	}
	pub fn rate(&self) -> f32 {
		self.rate
	}
}

impl Transformable for Emitter {
	fn transform(&self) -> &Transform {
		&self.transform
	}
	fn transform_to(&mut self, t: &Transform) {
		self.transform.position = t.position;
		self.transform.angle = t.angle;
	}
}

impl World {
	pub fn new<R>(res: &R, minion_gene_pool: &str) -> Self
		where R: ResourceLoader<u8> {
		let mut swarms = HashMap::new();
		let types = AgentType::all();
		for t in types {
			swarms.insert(*t, Swarm::new(*t));
		}
		fn default_gene_pool(_: io::Error) -> gen::GenePool {
			gen::GenePool::parse_from_base64(&["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
			                                   "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
			                                   "GzB2lQVwM00tTAm5gwajjf4wc0a5GzB2lQVwM00tTAm5gwajjf4wc0a5",
			                                   "GzB2lQdwM10vQEu5zwaPgDhfq2v8GzB2lQdwM10vQEu5zwaPgDhfq2v8"])
		}

		World {
			extent: Rect::new(-80., -80., 80., 80.),
			swarms: swarms,
			emitters: vec![Emitter::new(-20., -20., 0.4),
			               Emitter::new(-20., 20., 0.4),
			               Emitter::new(20., 20., 0.4),
			               Emitter::new(20., -20., 0.4)],
			minion_gene_pool: res.load(minion_gene_pool)
				.map(|data| gen::GenePool::parse_from_resource(&data))
				.unwrap_or_else(default_gene_pool),
			resource_gene_pool: gen::GenePool::parse_from_base64(&["GyA21QoQ", "M00sWS0M"]),
			registered: HashSet::new(),
		}
	}

	pub fn new_resource(&mut self, transform: &Transform, motion: Option<&Motion>) -> obj::Id {
		let mut gen = &mut self.resource_gene_pool.next();
		let id = self.swarm_mut(&AgentType::Resource)
			.spawn::<phen::Resource>(&mut gen, transform, motion, 0.8);
		self.register(id)
	}

	pub fn decay_to_resource(&mut self, transform: &Transform, dna: &gen::Dna) -> obj::Id {
		let id = self.swarm_mut(&AgentType::Resource)
			.spawn::<phen::Resource>(&mut gen::Genome::new(dna), transform, None, 0.8);
		self.register(id)
	}

	pub fn new_spore(&mut self, transform: &Transform, dna: &gen::Dna) -> obj::Id {
		let id = self.swarm_mut(&AgentType::Spore)
			.spawn::<phen::Spore>(&mut gen::Genome::new(dna).mutate(&mut rand::thread_rng()),
			                      transform,
			                      None,
			                      0.8);
		self.register(id)
	}

	pub fn hatch_spore(&mut self, transform: &Transform, dna: &gen::Dna) -> obj::Id {
		let id = self.swarm_mut(&AgentType::Minion)
			.spawn::<phen::Minion>(&mut gen::Genome::new(dna), transform, None, 0.3);
		self.register(id)
	}

	pub fn randomize_minion(&mut self, pos: Position, motion: Option<&Motion>) -> obj::Id {
		self.minion_gene_pool.randomize();
		self.new_minion(pos, motion)
	}

	pub fn init_minions(&mut self) {
		let n = self.minion_gene_pool.len();
		let mut r = self.extent.top_right().x * 0.25;
		let mut angle = 0.0f32;
		let angle_delta = consts::PI * 2. / 16. as f32;
		for _ in 0..n {
			let pos = Position::new(r * angle.cos(), r * angle.sin());
			let mut gen = self.minion_gene_pool.next();
			let id = self.swarm_mut(&AgentType::Minion)
				.spawn::<phen::Minion>(&mut gen,
				                       &Transform::new(pos, angle + consts::PI / 2.),
				                       None,
				                       0.3);
			self.register(id);
			angle += angle_delta;
			r += 1.;
		}
	}

	pub fn new_minion(&mut self, pos: Position, motion: Option<&Motion>) -> obj::Id {
		let angle = consts::PI / 2. + f32::atan2(pos.y, pos.x);
		let mut gen = self.minion_gene_pool.next();
		let id = self.swarm_mut(&AgentType::Minion)
			.spawn::<phen::Minion>(&mut gen, &Transform::new(pos, angle), motion, 0.3);
		self.register(id)
	}

	pub fn register(&mut self, id: obj::Id) -> obj::Id {
		self.registered.insert(id);
		id
	}

	pub fn registered(&mut self) -> Box<[Id]> {
		let collection = self.registered.iter().map(|r| *r).collect::<Vec<_>>().into_boxed_slice();
		self.registered.clear();
		collection
	}

	#[allow(dead_code)]
	pub fn agent(&self, id: obj::Id) -> Option<&Agent> {
		self.swarms.get(&id.type_of()).and_then(|m| m.get(id))
	}

	pub fn agent_mut(&mut self, id: obj::Id) -> Option<&mut Agent> {
		self.swarms.get_mut(&id.type_of()).and_then(|m| m.get_mut(id))
	}

	pub fn agents(&self, agent_type: AgentType) -> &agent::AgentMap {
		self.swarms.get(&agent_type).unwrap().agents()
	}

	pub fn agents_mut(&mut self, agent_type: AgentType) -> &mut agent::AgentMap {
		self.swarms.get_mut(&agent_type).unwrap().agents_mut()
	}

	pub fn swarm_mut(&mut self, agent_type: &AgentType) -> &mut Swarm {
		self.swarms.get_mut(&agent_type).unwrap()
	}

	pub fn emitters(&self) -> &[Emitter] {
		self.emitters.as_slice()
	}

	pub fn swarms(&self) -> &SwarmMap {
		&self.swarms
	}

	pub fn sweep(&mut self) -> Box<[Agent]> {
		let mut v = Vec::new();
		for (_, agents) in self.swarms.iter_mut() {
			agents.free_resources(&mut v);
		}
		v.into_boxed_slice()
	}

	pub fn dump(&self) -> io::Result<String> {
		let now: DateTime<UTC> = UTC::now();
		let file_name = now.format("rust-oids_%Y%m%d_%H%M%S.log").to_string();
		let mut f = try!(fs::File::create(&file_name));
		for (_, swarm) in self.swarms.iter().filter(|&(_, s)| !s.is_empty()) {
			try!(f.write_fmt(format_args!("[{}]\n", swarm.type_of())));
			for (_, agent) in swarm.agents().iter() {
				info!("{}: {}",
				      swarm.type_of(),
				      agent.dna().to_base64(base64::STANDARD));
				try!(f.write_fmt(format_args!("{}\n", agent.dna().to_base64(base64::STANDARD))));
			}
		}
		Ok(file_name)
	}
}
<commit_msg>Dumps now saves only minions in gene pool format, for easier reloading.<commit_after>pub mod segment;
pub mod agent;
pub mod swarm;
pub mod gen;
pub mod phen;

use backend::obj;
use backend::obj::*;
use rand;
use chrono::*;
use std::f32::consts;
use std::collections::HashMap;
use std::collections::HashSet;
use std::io;
use std::io::Write;
use std::fs;

use core::geometry::*;
use core::resource::ResourceLoader;
use backend::world::agent::Agent;
use backend::world::agent::AgentType;
use backend::world::agent::TypedAgent;
use backend::world::swarm::*;
use serialize::base64::{self, ToBase64};

pub struct World {
	pub extent: Rect,
	swarms: HashMap<AgentType, Swarm>,
	emitters: Vec<Emitter>,
	registered: HashSet<Id>,
	minion_gene_pool: gen::GenePool,
	resource_gene_pool: gen::GenePool,
}

pub trait WorldState {
	fn agent(&self, id: obj::Id) -> Option<&Agent>;
}

impl WorldState for World {
	fn agent(&self, id: obj::Id) -> Option<&Agent> {
		self.swarms.get(&id.type_of()).and_then(|m| m.get(id))
	}
}

#[derive(Clone)]
pub struct Emitter {
	transform: Transform,
	rate: f32,
}

impl Emitter {
	pub fn new(x: f32, y: f32, rate: f32) -> Self {
		Emitter {
			transform: Transform::from_position(Position::new(x, y)),
			rate: rate,
		}
	}
	pub fn rate(&self) -> f32 {
		self.rate
	}
}

impl Transformable for Emitter {
	fn transform(&self) -> &Transform {
		&self.transform
	}
	fn transform_to(&mut self, t: &Transform) {
		self.transform.position = t.position;
		self.transform.angle = t.angle;
	}
}

impl World {
	pub fn new<R>(res: &R, minion_gene_pool: &str) -> Self
		where R: ResourceLoader<u8> {
		let mut swarms = HashMap::new();
		let types = AgentType::all();
		for t in types {
			swarms.insert(*t, Swarm::new(*t));
		}
		fn default_gene_pool(_: io::Error) -> gen::GenePool {
			gen::GenePool::parse_from_base64(&["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
			                                   "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
			                                   "GzB2lQVwM00tTAm5gwajjf4wc0a5GzB2lQVwM00tTAm5gwajjf4wc0a5",
			                                   "GzB2lQdwM10vQEu5zwaPgDhfq2v8GzB2lQdwM10vQEu5zwaPgDhfq2v8"])
		}

		World {
			extent: Rect::new(-80., -80., 80., 80.),
			swarms: swarms,
			emitters: vec![Emitter::new(-20., -20., 0.4),
			               Emitter::new(-20., 20., 0.4),
			               Emitter::new(20., 20., 0.4),
			               Emitter::new(20., -20., 0.4)],
			minion_gene_pool: res.load(minion_gene_pool)
				.map(|data| gen::GenePool::parse_from_resource(&data))
				.unwrap_or_else(default_gene_pool),
			resource_gene_pool: gen::GenePool::parse_from_base64(&["GyA21QoQ", "M00sWS0M"]),
			registered: HashSet::new(),
		}
	}

	pub fn new_resource(&mut self, transform: &Transform, motion: Option<&Motion>) -> obj::Id {
		let mut gen = &mut self.resource_gene_pool.next();
		let id = self.swarm_mut(&AgentType::Resource)
			.spawn::<phen::Resource>(&mut gen, transform, motion, 0.8);
		self.register(id)
	}

	pub fn decay_to_resource(&mut self, transform: &Transform, dna: &gen::Dna) -> obj::Id {
		let id = self.swarm_mut(&AgentType::Resource)
			.spawn::<phen::Resource>(&mut gen::Genome::new(dna), transform, None, 0.8);
		self.register(id)
	}

	pub fn new_spore(&mut self, transform: &Transform, dna: &gen::Dna) -> obj::Id {
		let id = self.swarm_mut(&AgentType::Spore)
			.spawn::<phen::Spore>(&mut gen::Genome::new(dna).mutate(&mut rand::thread_rng()),
			                      transform,
			                      None,
			                      0.8);
		self.register(id)
	}

	pub fn hatch_spore(&mut self, transform: &Transform, dna: &gen::Dna) -> obj::Id {
		let id = self.swarm_mut(&AgentType::Minion)
			.spawn::<phen::Minion>(&mut gen::Genome::new(dna), transform, None, 0.3);
		self.register(id)
	}

	pub fn randomize_minion(&mut self, pos: Position, motion: Option<&Motion>) -> obj::Id {
		self.minion_gene_pool.randomize();
		self.new_minion(pos, motion)
	}

	pub fn init_minions(&mut self) {
		let n = self.minion_gene_pool.len();
		let mut r = self.extent.top_right().x * 0.25;
		let mut angle = 0.0f32;
		let angle_delta = consts::PI * 2. / 16. as f32;
		for _ in 0..n {
			let pos = Position::new(r * angle.cos(), r * angle.sin());
			let mut gen = self.minion_gene_pool.next();
			let id = self.swarm_mut(&AgentType::Minion)
				.spawn::<phen::Minion>(&mut gen,
				                       &Transform::new(pos, angle + consts::PI / 2.),
				                       None,
				                       0.3);
			self.register(id);
			angle += angle_delta;
			r += 1.;
		}
	}

	pub fn new_minion(&mut self, pos: Position, motion: Option<&Motion>) -> obj::Id {
		let angle = consts::PI / 2. + f32::atan2(pos.y, pos.x);
		let mut gen = self.minion_gene_pool.next();
		let id = self.swarm_mut(&AgentType::Minion)
			.spawn::<phen::Minion>(&mut gen, &Transform::new(pos, angle), motion, 0.3);
		self.register(id)
	}

	pub fn register(&mut self, id: obj::Id) -> obj::Id {
		self.registered.insert(id);
		id
	}

	pub fn registered(&mut self) -> Box<[Id]> {
		let collection = self.registered.iter().map(|r| *r).collect::<Vec<_>>().into_boxed_slice();
		self.registered.clear();
		collection
	}

	#[allow(dead_code)]
	pub fn agent(&self, id: obj::Id) -> Option<&Agent> {
		self.swarms.get(&id.type_of()).and_then(|m| m.get(id))
	}

	pub fn agent_mut(&mut self, id: obj::Id) -> Option<&mut Agent> {
		self.swarms.get_mut(&id.type_of()).and_then(|m| m.get_mut(id))
	}

	pub fn agents(&self, agent_type: AgentType) -> &agent::AgentMap {
		self.swarms.get(&agent_type).unwrap().agents()
	}

	pub fn agents_mut(&mut self, agent_type: AgentType) -> &mut agent::AgentMap {
		self.swarms.get_mut(&agent_type).unwrap().agents_mut()
	}

	pub fn swarm_mut(&mut self, agent_type: &AgentType) -> &mut Swarm {
		self.swarms.get_mut(&agent_type).unwrap()
	}

	pub fn emitters(&self) -> &[Emitter] {
		self.emitters.as_slice()
	}

	pub fn swarms(&self) -> &SwarmMap {
		&self.swarms
	}

	pub fn sweep(&mut self) -> Box<[Agent]> {
		let mut v = Vec::new();
		for (_, agents) in self.swarms.iter_mut() {
			agents.free_resources(&mut v);
		}
		v.into_boxed_slice()
	}

	pub fn dump(&self) -> io::Result<String> {
		let now: DateTime<UTC> = UTC::now();
		let file_name = now.format("resources/%Y%m%d_%H%M%S.csv").to_string();
		let mut f = try!(fs::File::create(&file_name));
		for (_, agent) in self.agents(agent::AgentType::Minion).iter() {
			info!("{}", agent.dna().to_base64(base64::STANDARD));
			try!(f.write_fmt(format_args!("{}\n", agent.dna().to_base64(base64::STANDARD))));
		}
		Ok(file_name)
	}
}
<|endoftext|>


<commit_before>//! Types and traits for TLS adaptors.
pub use priv_io::Stream;

use std::error::Error;
use std::io::prelude::*;
use std::fmt;

#[cfg(feature = "with-openssl")]
pub mod openssl;
#[cfg(feature = "with-security-framework")]
pub mod security_framework;

/// A trait implemented by TLS adaptors.
pub trait TlsStream: fmt::Debug + Read + Write + Send {
    /// Returns a reference to the underlying `Stream`.
    fn get_ref(&self) -> &Stream;

    /// Returns a mutable reference to the underlying `Stream`.
    fn get_mut(&mut self) -> &mut Stream;
}

/// A trait implemented by types that can negotiate TLS over a Postgres stream.
pub trait TlsHandshake: fmt::Debug {
    /// Performs a client-side TLS handshake, returning a wrapper around the
    /// provided stream.
    ///
    /// The host portion of the connection parameters is provided for hostname
    /// verification.
    fn tls_handshake(&self,
                     host: &str,
                     stream: Stream)
                     -> Result<Box<TlsStream>, Box<Error + Sync + Send>>;
}
<commit_msg>Doc tweaks<commit_after>//! Types and traits for TLS support.
pub use priv_io::Stream;

use std::error::Error;
use std::io::prelude::*;
use std::fmt;

#[cfg(feature = "with-openssl")]
pub mod openssl;
#[cfg(feature = "with-security-framework")]
pub mod security_framework;

/// A trait implemented by TLS streams.
pub trait TlsStream: fmt::Debug + Read + Write + Send {
    /// Returns a reference to the underlying `Stream`.
    fn get_ref(&self) -> &Stream;

    /// Returns a mutable reference to the underlying `Stream`.
    fn get_mut(&mut self) -> &mut Stream;
}

/// A trait implemented by types that can initiate a TLS session over a Postgres
/// stream.
pub trait TlsHandshake: fmt::Debug {
    /// Performs a client-side TLS handshake, returning a wrapper around the
    /// provided stream.
    ///
    /// The host portion of the connection parameters is provided for hostname
    /// verification.
    fn tls_handshake(&self,
                     host: &str,
                     stream: Stream)
                     -> Result<Box<TlsStream>, Box<Error + Sync + Send>>;
}
<|endoftext|>


<commit_before>//! Implementation of the install aspects of the compiler.
//!
//! This module is responsible for installing the standard library,
//! compiler, and documentation.

use std::env;
use std::fs;
use std::path::{Component, Path, PathBuf};
use std::process::Command;

use build_helper::t;

use crate::dist::{self, pkgname, sanitize_sh, tmpdir};
use crate::Compiler;

use crate::builder::{Builder, RunConfig, ShouldRun, Step};
use crate::cache::Interned;
use crate::config::Config;

pub fn install_docs(builder: &Builder<'_>, stage: u32, host: Interned<String>) {
    install_sh(builder, "docs", "rust-docs", stage, Some(host));
}

pub fn install_std(builder: &Builder<'_>, stage: u32, target: Interned<String>) {
    install_sh(builder, "std", "rust-std", stage, Some(target));
}

pub fn install_cargo(builder: &Builder<'_>, stage: u32, host: Interned<String>) {
    install_sh(builder, "cargo", "cargo", stage, Some(host));
}

pub fn install_rls(builder: &Builder<'_>, stage: u32, host: Interned<String>) {
    install_sh(builder, "rls", "rls", stage, Some(host));
}
pub fn install_clippy(builder: &Builder<'_>, stage: u32, host: Interned<String>) {
    install_sh(builder, "clippy", "clippy", stage, Some(host));
}
pub fn install_miri(builder: &Builder<'_>, stage: u32, host: Interned<String>) {
    install_sh(builder, "miri", "miri", stage, Some(host));
}

pub fn install_rustfmt(builder: &Builder<'_>, stage: u32, host: Interned<String>) {
    install_sh(builder, "rustfmt", "rustfmt", stage, Some(host));
}

pub fn install_analysis(builder: &Builder<'_>, stage: u32, host: Interned<String>) {
    install_sh(builder, "analysis", "rust-analysis", stage, Some(host));
}

pub fn install_src(builder: &Builder<'_>, stage: u32) {
    install_sh(builder, "src", "rust-src", stage, None);
}
pub fn install_rustc(builder: &Builder<'_>, stage: u32, host: Interned<String>) {
    install_sh(builder, "rustc", "rustc", stage, Some(host));
}

fn install_sh(
    builder: &Builder<'_>,
    package: &str,
    name: &str,
    stage: u32,
    host: Option<Interned<String>>,
) {
    builder.info(&format!("Install {} stage{} ({:?})", package, stage, host));

    let prefix_default = PathBuf::from("/usr/local");
    let sysconfdir_default = PathBuf::from("/etc");
    let datadir_default = PathBuf::from("share");
    let docdir_default = datadir_default.join("doc/rust");
    let libdir_default = PathBuf::from("lib");
    let mandir_default = datadir_default.join("man");
    let prefix = builder.config.prefix.as_ref().map_or(prefix_default, |p| {
        fs::canonicalize(p).unwrap_or_else(|_| panic!("could not canonicalize {}", p.display()))
    });
    let sysconfdir = builder.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default);
    let datadir = builder.config.datadir.as_ref().unwrap_or(&datadir_default);
    let docdir = builder.config.docdir.as_ref().unwrap_or(&docdir_default);
    let bindir = &builder.config.bindir;
    let libdir = builder.config.libdir.as_ref().unwrap_or(&libdir_default);
    let mandir = builder.config.mandir.as_ref().unwrap_or(&mandir_default);

    let sysconfdir = prefix.join(sysconfdir);
    let datadir = prefix.join(datadir);
    let docdir = prefix.join(docdir);
    let bindir = prefix.join(bindir);
    let libdir = prefix.join(libdir);
    let mandir = prefix.join(mandir);

    let destdir = env::var_os("DESTDIR").map(PathBuf::from);

    let prefix = add_destdir(&prefix, &destdir);
    let sysconfdir = add_destdir(&sysconfdir, &destdir);
    let datadir = add_destdir(&datadir, &destdir);
    let docdir = add_destdir(&docdir, &destdir);
    let bindir = add_destdir(&bindir, &destdir);
    let libdir = add_destdir(&libdir, &destdir);
    let mandir = add_destdir(&mandir, &destdir);

    let empty_dir = builder.out.join("tmp/empty_dir");

    t!(fs::create_dir_all(&empty_dir));
    let package_name = if let Some(host) = host {
        format!("{}-{}", pkgname(builder, name), host)
    } else {
        pkgname(builder, name)
    };

    let mut cmd = Command::new("sh");
    cmd.current_dir(&empty_dir)
        .arg(sanitize_sh(&tmpdir(builder).join(&package_name).join("install.sh")))
        .arg(format!("--prefix={}", sanitize_sh(&prefix)))
        .arg(format!("--sysconfdir={}", sanitize_sh(&sysconfdir)))
        .arg(format!("--datadir={}", sanitize_sh(&datadir)))
        .arg(format!("--docdir={}", sanitize_sh(&docdir)))
        .arg(format!("--bindir={}", sanitize_sh(&bindir)))
        .arg(format!("--libdir={}", sanitize_sh(&libdir)))
        .arg(format!("--mandir={}", sanitize_sh(&mandir)))
        .arg("--disable-ldconfig");
    builder.run(&mut cmd);
    t!(fs::remove_dir_all(&empty_dir));
}

fn add_destdir(path: &Path, destdir: &Option<PathBuf>) -> PathBuf {
    let mut ret = match *destdir {
        Some(ref dest) => dest.clone(),
        None => return path.to_path_buf(),
    };
    for part in path.components() {
        if let Component::Normal(s) = part {
            ret.push(s)
        }
    }
    ret
}

macro_rules! install {
    (($sel:ident, $builder:ident, $_config:ident),
       $($name:ident,
       $path:expr,
       $default_cond:expr,
       only_hosts: $only_hosts:expr,
       $run_item:block $(, $c:ident)*;)+) => {
        $(
            #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
        pub struct $name {
            pub compiler: Compiler,
            pub target: Interned<String>,
        }

        impl $name {
            #[allow(dead_code)]
            fn should_build(config: &Config) -> bool {
                config.extended && config.tools.as_ref()
                    .map_or(true, |t| t.contains($path))
            }

            #[allow(dead_code)]
            fn should_install(builder: &Builder<'_>) -> bool {
                builder.config.tools.as_ref().map_or(false, |t| t.contains($path))
            }
        }

        impl Step for $name {
            type Output = ();
            const DEFAULT: bool = true;
            const ONLY_HOSTS: bool = $only_hosts;
            $(const $c: bool = true;)*

            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
                let $_config = &run.builder.config;
                run.path($path).default_condition($default_cond)
            }

            fn make_run(run: RunConfig<'_>) {
                run.builder.ensure($name {
                    compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
                    target: run.target,
                });
            }

            fn run($sel, $builder: &Builder<'_>) {
                $run_item
            }
        })+
    }
}

install!((self, builder, _config),
    Docs, "src/doc", _config.docs, only_hosts: false, {
        builder.ensure(dist::Docs { host: self.target });
        install_docs(builder, self.compiler.stage, self.target);
    };
    Std, "src/libstd", true, only_hosts: true, {
        for target in &builder.targets {
            builder.ensure(dist::Std {
                compiler: self.compiler,
                target: *target
            });
            install_std(builder, self.compiler.stage, *target);
        }
    };
    Cargo, "cargo", Self::should_build(_config), only_hosts: true, {
        builder.ensure(dist::Cargo { compiler: self.compiler, target: self.target });
        install_cargo(builder, self.compiler.stage, self.target);
    };
    Rls, "rls", Self::should_build(_config), only_hosts: true, {
        if builder.ensure(dist::Rls { compiler: self.compiler, target: self.target }).is_some() ||
            Self::should_install(builder) {
            install_rls(builder, self.compiler.stage, self.target);
        } else {
            builder.info(
                &format!("skipping Install RLS stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    Clippy, "clippy", Self::should_build(_config), only_hosts: true, {
        builder.ensure(dist::Clippy { compiler: self.compiler, target: self.target });
        if Self::should_install(builder) {
            install_clippy(builder, self.compiler.stage, self.target);
        } else {
            builder.info(
                &format!("skipping Install clippy stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    Miri, "miri", Self::should_build(_config), only_hosts: true, {
        if builder.ensure(dist::Miri { compiler: self.compiler, target: self.target }).is_some() ||
            Self::should_install(builder) {
            install_miri(builder, self.compiler.stage, self.target);
        } else {
            builder.info(
                &format!("skipping Install miri stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    Rustfmt, "rustfmt", Self::should_build(_config), only_hosts: true, {
        if builder.ensure(dist::Rustfmt {
            compiler: self.compiler,
            target: self.target
        }).is_some() || Self::should_install(builder) {
            install_rustfmt(builder, self.compiler.stage, self.target);
        } else {
            builder.info(
                &format!("skipping Install Rustfmt stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    Analysis, "analysis", Self::should_build(_config), only_hosts: false, {
        builder.ensure(dist::Analysis {
            // Find the actual compiler (handling the full bootstrap option) which
            // produced the save-analysis data because that data isn't copied
            // through the sysroot uplifting.
            compiler: builder.compiler_for(builder.top_stage, builder.config.build, self.target),
            target: self.target
        });
        install_analysis(builder, self.compiler.stage, self.target);
    };
    Rustc, "src/librustc", true, only_hosts: true, {
        builder.ensure(dist::Rustc {
            compiler: builder.compiler(builder.top_stage, self.target),
        });
        install_rustc(builder, self.compiler.stage, self.target);
    };
);

#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Src {
    pub stage: u32,
}

impl Step for Src {
    type Output = ();
    const DEFAULT: bool = true;
    const ONLY_HOSTS: bool = true;

    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
        let config = &run.builder.config;
        let cond = config.extended && config.tools.as_ref().map_or(true, |t| t.contains("src"));
        run.path("src").default_condition(cond)
    }

    fn make_run(run: RunConfig<'_>) {
        run.builder.ensure(Src { stage: run.builder.top_stage });
    }

    fn run(self, builder: &Builder<'_>) {
        builder.ensure(dist::Src);
        install_src(builder, self.stage);
    }
}
<commit_msg>bootstrap/install.rs: support a nonexistent `prefix` in `x.py install`<commit_after>//! Implementation of the install aspects of the compiler.
//!
//! This module is responsible for installing the standard library,
//! compiler, and documentation.

use std::env;
use std::fs;
use std::path::{Component, Path, PathBuf};
use std::process::Command;

use build_helper::t;

use crate::dist::{self, pkgname, sanitize_sh, tmpdir};
use crate::Compiler;

use crate::builder::{Builder, RunConfig, ShouldRun, Step};
use crate::cache::Interned;
use crate::config::Config;

pub fn install_docs(builder: &Builder<'_>, stage: u32, host: Interned<String>) {
    install_sh(builder, "docs", "rust-docs", stage, Some(host));
}

pub fn install_std(builder: &Builder<'_>, stage: u32, target: Interned<String>) {
    install_sh(builder, "std", "rust-std", stage, Some(target));
}

pub fn install_cargo(builder: &Builder<'_>, stage: u32, host: Interned<String>) {
    install_sh(builder, "cargo", "cargo", stage, Some(host));
}

pub fn install_rls(builder: &Builder<'_>, stage: u32, host: Interned<String>) {
    install_sh(builder, "rls", "rls", stage, Some(host));
}
pub fn install_clippy(builder: &Builder<'_>, stage: u32, host: Interned<String>) {
    install_sh(builder, "clippy", "clippy", stage, Some(host));
}
pub fn install_miri(builder: &Builder<'_>, stage: u32, host: Interned<String>) {
    install_sh(builder, "miri", "miri", stage, Some(host));
}

pub fn install_rustfmt(builder: &Builder<'_>, stage: u32, host: Interned<String>) {
    install_sh(builder, "rustfmt", "rustfmt", stage, Some(host));
}

pub fn install_analysis(builder: &Builder<'_>, stage: u32, host: Interned<String>) {
    install_sh(builder, "analysis", "rust-analysis", stage, Some(host));
}

pub fn install_src(builder: &Builder<'_>, stage: u32) {
    install_sh(builder, "src", "rust-src", stage, None);
}
pub fn install_rustc(builder: &Builder<'_>, stage: u32, host: Interned<String>) {
    install_sh(builder, "rustc", "rustc", stage, Some(host));
}

fn install_sh(
    builder: &Builder<'_>,
    package: &str,
    name: &str,
    stage: u32,
    host: Option<Interned<String>>,
) {
    builder.info(&format!("Install {} stage{} ({:?})", package, stage, host));

    let prefix_default = PathBuf::from("/usr/local");
    let sysconfdir_default = PathBuf::from("/etc");
    let datadir_default = PathBuf::from("share");
    let docdir_default = datadir_default.join("doc/rust");
    let libdir_default = PathBuf::from("lib");
    let mandir_default = datadir_default.join("man");
    let prefix = builder.config.prefix.as_ref().map_or(prefix_default, |p| {
        fs::create_dir_all(p)
            .unwrap_or_else(|err| panic!("could not create {}: {}", p.display(), err));
        fs::canonicalize(p)
            .unwrap_or_else(|err| panic!("could not canonicalize {}: {}", p.display(), err))
    });
    let sysconfdir = builder.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default);
    let datadir = builder.config.datadir.as_ref().unwrap_or(&datadir_default);
    let docdir = builder.config.docdir.as_ref().unwrap_or(&docdir_default);
    let bindir = &builder.config.bindir;
    let libdir = builder.config.libdir.as_ref().unwrap_or(&libdir_default);
    let mandir = builder.config.mandir.as_ref().unwrap_or(&mandir_default);

    let sysconfdir = prefix.join(sysconfdir);
    let datadir = prefix.join(datadir);
    let docdir = prefix.join(docdir);
    let bindir = prefix.join(bindir);
    let libdir = prefix.join(libdir);
    let mandir = prefix.join(mandir);

    let destdir = env::var_os("DESTDIR").map(PathBuf::from);

    let prefix = add_destdir(&prefix, &destdir);
    let sysconfdir = add_destdir(&sysconfdir, &destdir);
    let datadir = add_destdir(&datadir, &destdir);
    let docdir = add_destdir(&docdir, &destdir);
    let bindir = add_destdir(&bindir, &destdir);
    let libdir = add_destdir(&libdir, &destdir);
    let mandir = add_destdir(&mandir, &destdir);

    let empty_dir = builder.out.join("tmp/empty_dir");

    t!(fs::create_dir_all(&empty_dir));
    let package_name = if let Some(host) = host {
        format!("{}-{}", pkgname(builder, name), host)
    } else {
        pkgname(builder, name)
    };

    let mut cmd = Command::new("sh");
    cmd.current_dir(&empty_dir)
        .arg(sanitize_sh(&tmpdir(builder).join(&package_name).join("install.sh")))
        .arg(format!("--prefix={}", sanitize_sh(&prefix)))
        .arg(format!("--sysconfdir={}", sanitize_sh(&sysconfdir)))
        .arg(format!("--datadir={}", sanitize_sh(&datadir)))
        .arg(format!("--docdir={}", sanitize_sh(&docdir)))
        .arg(format!("--bindir={}", sanitize_sh(&bindir)))
        .arg(format!("--libdir={}", sanitize_sh(&libdir)))
        .arg(format!("--mandir={}", sanitize_sh(&mandir)))
        .arg("--disable-ldconfig");
    builder.run(&mut cmd);
    t!(fs::remove_dir_all(&empty_dir));
}

fn add_destdir(path: &Path, destdir: &Option<PathBuf>) -> PathBuf {
    let mut ret = match *destdir {
        Some(ref dest) => dest.clone(),
        None => return path.to_path_buf(),
    };
    for part in path.components() {
        if let Component::Normal(s) = part {
            ret.push(s)
        }
    }
    ret
}

macro_rules! install {
    (($sel:ident, $builder:ident, $_config:ident),
       $($name:ident,
       $path:expr,
       $default_cond:expr,
       only_hosts: $only_hosts:expr,
       $run_item:block $(, $c:ident)*;)+) => {
        $(
            #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
        pub struct $name {
            pub compiler: Compiler,
            pub target: Interned<String>,
        }

        impl $name {
            #[allow(dead_code)]
            fn should_build(config: &Config) -> bool {
                config.extended && config.tools.as_ref()
                    .map_or(true, |t| t.contains($path))
            }

            #[allow(dead_code)]
            fn should_install(builder: &Builder<'_>) -> bool {
                builder.config.tools.as_ref().map_or(false, |t| t.contains($path))
            }
        }

        impl Step for $name {
            type Output = ();
            const DEFAULT: bool = true;
            const ONLY_HOSTS: bool = $only_hosts;
            $(const $c: bool = true;)*

            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
                let $_config = &run.builder.config;
                run.path($path).default_condition($default_cond)
            }

            fn make_run(run: RunConfig<'_>) {
                run.builder.ensure($name {
                    compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
                    target: run.target,
                });
            }

            fn run($sel, $builder: &Builder<'_>) {
                $run_item
            }
        })+
    }
}

install!((self, builder, _config),
    Docs, "src/doc", _config.docs, only_hosts: false, {
        builder.ensure(dist::Docs { host: self.target });
        install_docs(builder, self.compiler.stage, self.target);
    };
    Std, "src/libstd", true, only_hosts: true, {
        for target in &builder.targets {
            builder.ensure(dist::Std {
                compiler: self.compiler,
                target: *target
            });
            install_std(builder, self.compiler.stage, *target);
        }
    };
    Cargo, "cargo", Self::should_build(_config), only_hosts: true, {
        builder.ensure(dist::Cargo { compiler: self.compiler, target: self.target });
        install_cargo(builder, self.compiler.stage, self.target);
    };
    Rls, "rls", Self::should_build(_config), only_hosts: true, {
        if builder.ensure(dist::Rls { compiler: self.compiler, target: self.target }).is_some() ||
            Self::should_install(builder) {
            install_rls(builder, self.compiler.stage, self.target);
        } else {
            builder.info(
                &format!("skipping Install RLS stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    Clippy, "clippy", Self::should_build(_config), only_hosts: true, {
        builder.ensure(dist::Clippy { compiler: self.compiler, target: self.target });
        if Self::should_install(builder) {
            install_clippy(builder, self.compiler.stage, self.target);
        } else {
            builder.info(
                &format!("skipping Install clippy stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    Miri, "miri", Self::should_build(_config), only_hosts: true, {
        if builder.ensure(dist::Miri { compiler: self.compiler, target: self.target }).is_some() ||
            Self::should_install(builder) {
            install_miri(builder, self.compiler.stage, self.target);
        } else {
            builder.info(
                &format!("skipping Install miri stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    Rustfmt, "rustfmt", Self::should_build(_config), only_hosts: true, {
        if builder.ensure(dist::Rustfmt {
            compiler: self.compiler,
            target: self.target
        }).is_some() || Self::should_install(builder) {
            install_rustfmt(builder, self.compiler.stage, self.target);
        } else {
            builder.info(
                &format!("skipping Install Rustfmt stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    Analysis, "analysis", Self::should_build(_config), only_hosts: false, {
        builder.ensure(dist::Analysis {
            // Find the actual compiler (handling the full bootstrap option) which
            // produced the save-analysis data because that data isn't copied
            // through the sysroot uplifting.
            compiler: builder.compiler_for(builder.top_stage, builder.config.build, self.target),
            target: self.target
        });
        install_analysis(builder, self.compiler.stage, self.target);
    };
    Rustc, "src/librustc", true, only_hosts: true, {
        builder.ensure(dist::Rustc {
            compiler: builder.compiler(builder.top_stage, self.target),
        });
        install_rustc(builder, self.compiler.stage, self.target);
    };
);

#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Src {
    pub stage: u32,
}

impl Step for Src {
    type Output = ();
    const DEFAULT: bool = true;
    const ONLY_HOSTS: bool = true;

    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
        let config = &run.builder.config;
        let cond = config.extended && config.tools.as_ref().map_or(true, |t| t.contains("src"));
        run.path("src").default_condition(cond)
    }

    fn make_run(run: RunConfig<'_>) {
        run.builder.ensure(Src { stage: run.builder.top_stage });
    }

    fn run(self, builder: &Builder<'_>) {
        builder.ensure(dist::Src);
        install_src(builder, self.stage);
    }
}
<|endoftext|>


<commit_before>//! Implementation of the install aspects of the compiler.
//!
//! This module is responsible for installing the standard library,
//! compiler, and documentation.

use std::env;
use std::fs;
use std::path::{Component, PathBuf};
use std::process::Command;

use build_helper::t;

use crate::dist::{self, sanitize_sh};
use crate::tarball::GeneratedTarball;
use crate::Compiler;

use crate::builder::{Builder, RunConfig, ShouldRun, Step};
use crate::config::{Config, TargetSelection};

#[cfg(target_os = "illumos")]
const SHELL: &str = "bash";
#[cfg(not(target_os = "illumos"))]
const SHELL: &str = "sh";

fn install_sh(
    builder: &Builder<'_>,
    package: &str,
    stage: u32,
    host: Option<TargetSelection>,
    tarball: &GeneratedTarball,
) {
    builder.info(&format!("Install {} stage{} ({:?})", package, stage, host));

    let prefix = default_path(&builder.config.prefix, "/usr/local");
    let sysconfdir = prefix.join(default_path(&builder.config.sysconfdir, "/etc"));
    let datadir = prefix.join(default_path(&builder.config.datadir, "share"));
    let docdir = prefix.join(default_path(&builder.config.docdir, "share/doc/rust"));
    let mandir = prefix.join(default_path(&builder.config.mandir, "share/man"));
    let libdir = prefix.join(default_path(&builder.config.libdir, "lib"));
    let bindir = prefix.join(&builder.config.bindir); // Default in config.rs

    let empty_dir = builder.out.join("tmp/empty_dir");
    t!(fs::create_dir_all(&empty_dir));

    let mut cmd = Command::new(SHELL);
    cmd.current_dir(&empty_dir)
        .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh")))
        .arg(format!("--prefix={}", prepare_dir(prefix)))
        .arg(format!("--sysconfdir={}", prepare_dir(sysconfdir)))
        .arg(format!("--datadir={}", prepare_dir(datadir)))
        .arg(format!("--docdir={}", prepare_dir(docdir)))
        .arg(format!("--bindir={}", prepare_dir(bindir)))
        .arg(format!("--libdir={}", prepare_dir(libdir)))
        .arg(format!("--mandir={}", prepare_dir(mandir)))
        .arg("--disable-ldconfig");
    builder.run(&mut cmd);
    t!(fs::remove_dir_all(&empty_dir));
}

fn default_path(config: &Option<PathBuf>, default: &str) -> PathBuf {
    config.as_ref().cloned().unwrap_or_else(|| PathBuf::from(default))
}

fn prepare_dir(mut path: PathBuf) -> String {
    // The DESTDIR environment variable is a standard way to install software in a subdirectory
    // while keeping the original directory structure, even if the prefix or other directories
    // contain absolute paths.
    //
    // More information on the environment variable is available here:
    // https://www.gnu.org/prep/standards/html_node/DESTDIR.html
    if let Some(destdir) = env::var_os("DESTDIR").map(PathBuf::from) {
        let without_destdir = path.clone();
        path = destdir;
        // Custom .join() which ignores disk roots.
        for part in without_destdir.components() {
            if let Component::Normal(s) = part {
                path.push(s)
            }
        }
    }

    // The installation command is not executed from the current directory, but from a temporary
    // directory. To prevent relative paths from breaking this converts relative paths to absolute
    // paths. std::fs::canonicalize is not used as that requires the path to actually be present.
    if path.is_relative() {
        path = std::env::current_dir().expect("failed to get the current directory").join(path);
        assert!(path.is_absolute(), "could not make the path relative");
    }

    sanitize_sh(&path)
}

macro_rules! install {
    (($sel:ident, $builder:ident, $_config:ident),
       $($name:ident,
       $path:expr,
       $default_cond:expr,
       only_hosts: $only_hosts:expr,
       $run_item:block $(, $c:ident)*;)+) => {
        $(
            #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
        pub struct $name {
            pub compiler: Compiler,
            pub target: TargetSelection,
        }

        impl $name {
            #[allow(dead_code)]
            fn should_build(config: &Config) -> bool {
                config.extended && config.tools.as_ref()
                    .map_or(true, |t| t.contains($path))
            }
        }

        impl Step for $name {
            type Output = ();
            const DEFAULT: bool = true;
            const ONLY_HOSTS: bool = $only_hosts;
            $(const $c: bool = true;)*

            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
                let $_config = &run.builder.config;
                run.path($path).default_condition($default_cond)
            }

            fn make_run(run: RunConfig<'_>) {
                run.builder.ensure($name {
                    compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
                    target: run.target,
                });
            }

            fn run($sel, $builder: &Builder<'_>) {
                $run_item
            }
        })+
    }
}

install!((self, builder, _config),
    Docs, "src/doc", _config.docs, only_hosts: false, {
        let tarball = builder.ensure(dist::Docs { host: self.target }).expect("missing docs");
        install_sh(builder, "docs", self.compiler.stage, Some(self.target), &tarball);
    };
    Std, "library/std", true, only_hosts: false, {
        for target in &builder.targets {
            let tarball = builder.ensure(dist::Std {
                compiler: self.compiler,
                target: *target
            }).expect("missing std");
            install_sh(builder, "std", self.compiler.stage, Some(*target), &tarball);
        }
    };
    Cargo, "cargo", Self::should_build(_config), only_hosts: true, {
        let tarball = builder.ensure(dist::Cargo { compiler: self.compiler, target: self.target });
        install_sh(builder, "cargo", self.compiler.stage, Some(self.target), &tarball);
    };
    Rls, "rls", Self::should_build(_config), only_hosts: true, {
        if let Some(tarball) = builder.ensure(dist::Rls { compiler: self.compiler, target: self.target }) {
            install_sh(builder, "rls", self.compiler.stage, Some(self.target), &tarball);
        } else {
            builder.info(
                &format!("skipping Install RLS stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    RustAnalyzer, "rust-analyzer", Self::should_build(_config), only_hosts: true, {
        let tarball = builder
            .ensure(dist::RustAnalyzer { compiler: self.compiler, target: self.target })
            .expect("missing rust-analyzer");
        install_sh(builder, "rust-analyzer", self.compiler.stage, Some(self.target), &tarball);
    };
    Clippy, "clippy", Self::should_build(_config), only_hosts: true, {
        let tarball = builder.ensure(dist::Clippy { compiler: self.compiler, target: self.target });
        install_sh(builder, "clippy", self.compiler.stage, Some(self.target), &tarball);
    };
    Miri, "miri", Self::should_build(_config), only_hosts: true, {
        if let Some(tarball) = builder.ensure(dist::Miri { compiler: self.compiler, target: self.target }) {
            install_sh(builder, "miri", self.compiler.stage, Some(self.target), &tarball);
        } else {
            builder.info(
                &format!("skipping Install miri stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    Rustfmt, "rustfmt", Self::should_build(_config), only_hosts: true, {
        if let Some(tarball) = builder.ensure(dist::Rustfmt {
            compiler: self.compiler,
            target: self.target
        }) {
            install_sh(builder, "rustfmt", self.compiler.stage, Some(self.target), &tarball);
        } else {
            builder.info(
                &format!("skipping Install Rustfmt stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    RustDemangler, "rust-demangler", Self::should_build(_config), only_hosts: true, {
        // Note: Even though `should_build` may return true for `extended` default tools,
        // dist::RustDemangler may still return None, unless the target-dependent `profiler` config
        // is also true, or the `tools` array explicitly includes "rust-demangler".
        if let Some(tarball) = builder.ensure(dist::RustDemangler {
            compiler: self.compiler,
            target: self.target
        }) {
            install_sh(builder, "rust-demangler", self.compiler.stage, Some(self.target), &tarball);
        } else {
            builder.info(
                &format!("skipping Install RustDemangler stage{} ({})",
                         self.compiler.stage, self.target),
            );
        }
    };
    Analysis, "analysis", Self::should_build(_config), only_hosts: false, {
        let tarball = builder.ensure(dist::Analysis {
            // Find the actual compiler (handling the full bootstrap option) which
            // produced the save-analysis data because that data isn't copied
            // through the sysroot uplifting.
            compiler: builder.compiler_for(builder.top_stage, builder.config.build, self.target),
            target: self.target
        }).expect("missing analysis");
        install_sh(builder, "analysis", self.compiler.stage, Some(self.target), &tarball);
    };
    Rustc, "src/librustc", true, only_hosts: true, {
        let tarball = builder.ensure(dist::Rustc {
            compiler: builder.compiler(builder.top_stage, self.target),
        });
        install_sh(builder, "rustc", self.compiler.stage, Some(self.target), &tarball);
    };
);

#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Src {
    pub stage: u32,
}

impl Step for Src {
    type Output = ();
    const DEFAULT: bool = true;
    const ONLY_HOSTS: bool = true;

    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
        let config = &run.builder.config;
        let cond = config.extended && config.tools.as_ref().map_or(true, |t| t.contains("src"));
        run.path("src").default_condition(cond)
    }

    fn make_run(run: RunConfig<'_>) {
        run.builder.ensure(Src { stage: run.builder.top_stage });
    }

    fn run(self, builder: &Builder<'_>) {
        let tarball = builder.ensure(dist::Src);
        install_sh(builder, "src", self.stage, None, &tarball);
    }
}
<commit_msg>Rollup merge of #87007 - ehuss:fix-rust-analyzer-install, r=Mark-Simulacrum<commit_after>//! Implementation of the install aspects of the compiler.
//!
//! This module is responsible for installing the standard library,
//! compiler, and documentation.

use std::env;
use std::fs;
use std::path::{Component, PathBuf};
use std::process::Command;

use build_helper::t;

use crate::dist::{self, sanitize_sh};
use crate::tarball::GeneratedTarball;
use crate::Compiler;

use crate::builder::{Builder, RunConfig, ShouldRun, Step};
use crate::config::{Config, TargetSelection};

#[cfg(target_os = "illumos")]
const SHELL: &str = "bash";
#[cfg(not(target_os = "illumos"))]
const SHELL: &str = "sh";

fn install_sh(
    builder: &Builder<'_>,
    package: &str,
    stage: u32,
    host: Option<TargetSelection>,
    tarball: &GeneratedTarball,
) {
    builder.info(&format!("Install {} stage{} ({:?})", package, stage, host));

    let prefix = default_path(&builder.config.prefix, "/usr/local");
    let sysconfdir = prefix.join(default_path(&builder.config.sysconfdir, "/etc"));
    let datadir = prefix.join(default_path(&builder.config.datadir, "share"));
    let docdir = prefix.join(default_path(&builder.config.docdir, "share/doc/rust"));
    let mandir = prefix.join(default_path(&builder.config.mandir, "share/man"));
    let libdir = prefix.join(default_path(&builder.config.libdir, "lib"));
    let bindir = prefix.join(&builder.config.bindir); // Default in config.rs

    let empty_dir = builder.out.join("tmp/empty_dir");
    t!(fs::create_dir_all(&empty_dir));

    let mut cmd = Command::new(SHELL);
    cmd.current_dir(&empty_dir)
        .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh")))
        .arg(format!("--prefix={}", prepare_dir(prefix)))
        .arg(format!("--sysconfdir={}", prepare_dir(sysconfdir)))
        .arg(format!("--datadir={}", prepare_dir(datadir)))
        .arg(format!("--docdir={}", prepare_dir(docdir)))
        .arg(format!("--bindir={}", prepare_dir(bindir)))
        .arg(format!("--libdir={}", prepare_dir(libdir)))
        .arg(format!("--mandir={}", prepare_dir(mandir)))
        .arg("--disable-ldconfig");
    builder.run(&mut cmd);
    t!(fs::remove_dir_all(&empty_dir));
}

fn default_path(config: &Option<PathBuf>, default: &str) -> PathBuf {
    config.as_ref().cloned().unwrap_or_else(|| PathBuf::from(default))
}

fn prepare_dir(mut path: PathBuf) -> String {
    // The DESTDIR environment variable is a standard way to install software in a subdirectory
    // while keeping the original directory structure, even if the prefix or other directories
    // contain absolute paths.
    //
    // More information on the environment variable is available here:
    // https://www.gnu.org/prep/standards/html_node/DESTDIR.html
    if let Some(destdir) = env::var_os("DESTDIR").map(PathBuf::from) {
        let without_destdir = path.clone();
        path = destdir;
        // Custom .join() which ignores disk roots.
        for part in without_destdir.components() {
            if let Component::Normal(s) = part {
                path.push(s)
            }
        }
    }

    // The installation command is not executed from the current directory, but from a temporary
    // directory. To prevent relative paths from breaking this converts relative paths to absolute
    // paths. std::fs::canonicalize is not used as that requires the path to actually be present.
    if path.is_relative() {
        path = std::env::current_dir().expect("failed to get the current directory").join(path);
        assert!(path.is_absolute(), "could not make the path relative");
    }

    sanitize_sh(&path)
}

macro_rules! install {
    (($sel:ident, $builder:ident, $_config:ident),
       $($name:ident,
       $path:expr,
       $default_cond:expr,
       only_hosts: $only_hosts:expr,
       $run_item:block $(, $c:ident)*;)+) => {
        $(
            #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
        pub struct $name {
            pub compiler: Compiler,
            pub target: TargetSelection,
        }

        impl $name {
            #[allow(dead_code)]
            fn should_build(config: &Config) -> bool {
                config.extended && config.tools.as_ref()
                    .map_or(true, |t| t.contains($path))
            }
        }

        impl Step for $name {
            type Output = ();
            const DEFAULT: bool = true;
            const ONLY_HOSTS: bool = $only_hosts;
            $(const $c: bool = true;)*

            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
                let $_config = &run.builder.config;
                run.path($path).default_condition($default_cond)
            }

            fn make_run(run: RunConfig<'_>) {
                run.builder.ensure($name {
                    compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
                    target: run.target,
                });
            }

            fn run($sel, $builder: &Builder<'_>) {
                $run_item
            }
        })+
    }
}

install!((self, builder, _config),
    Docs, "src/doc", _config.docs, only_hosts: false, {
        if let Some(tarball) = builder.ensure(dist::Docs { host: self.target }) {
            install_sh(builder, "docs", self.compiler.stage, Some(self.target), &tarball);
        } else {
            panic!("docs are not available to install, \
                check that `build.docs` is true in `config.toml`");
        }
    };
    Std, "library/std", true, only_hosts: false, {
        for target in &builder.targets {
            // `expect` should be safe, only None when host != build, but this
            // only runs when host == build
            let tarball = builder.ensure(dist::Std {
                compiler: self.compiler,
                target: *target
            }).expect("missing std");
            install_sh(builder, "std", self.compiler.stage, Some(*target), &tarball);
        }
    };
    Cargo, "cargo", Self::should_build(_config), only_hosts: true, {
        let tarball = builder.ensure(dist::Cargo { compiler: self.compiler, target: self.target });
        install_sh(builder, "cargo", self.compiler.stage, Some(self.target), &tarball);
    };
    Rls, "rls", Self::should_build(_config), only_hosts: true, {
        if let Some(tarball) = builder.ensure(dist::Rls { compiler: self.compiler, target: self.target }) {
            install_sh(builder, "rls", self.compiler.stage, Some(self.target), &tarball);
        } else {
            builder.info(
                &format!("skipping Install RLS stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    RustAnalyzer, "rust-analyzer", Self::should_build(_config), only_hosts: true, {
        if let Some(tarball) =
            builder.ensure(dist::RustAnalyzer { compiler: self.compiler, target: self.target })
        {
            install_sh(builder, "rust-analyzer", self.compiler.stage, Some(self.target), &tarball);
        } else {
            builder.info(
                &format!("skipping Install rust-analyzer stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    Clippy, "clippy", Self::should_build(_config), only_hosts: true, {
        let tarball = builder.ensure(dist::Clippy { compiler: self.compiler, target: self.target });
        install_sh(builder, "clippy", self.compiler.stage, Some(self.target), &tarball);
    };
    Miri, "miri", Self::should_build(_config), only_hosts: true, {
        if let Some(tarball) = builder.ensure(dist::Miri { compiler: self.compiler, target: self.target }) {
            install_sh(builder, "miri", self.compiler.stage, Some(self.target), &tarball);
        } else {
            builder.info(
                &format!("skipping Install miri stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    Rustfmt, "rustfmt", Self::should_build(_config), only_hosts: true, {
        if let Some(tarball) = builder.ensure(dist::Rustfmt {
            compiler: self.compiler,
            target: self.target
        }) {
            install_sh(builder, "rustfmt", self.compiler.stage, Some(self.target), &tarball);
        } else {
            builder.info(
                &format!("skipping Install Rustfmt stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    RustDemangler, "rust-demangler", Self::should_build(_config), only_hosts: true, {
        // Note: Even though `should_build` may return true for `extended` default tools,
        // dist::RustDemangler may still return None, unless the target-dependent `profiler` config
        // is also true, or the `tools` array explicitly includes "rust-demangler".
        if let Some(tarball) = builder.ensure(dist::RustDemangler {
            compiler: self.compiler,
            target: self.target
        }) {
            install_sh(builder, "rust-demangler", self.compiler.stage, Some(self.target), &tarball);
        } else {
            builder.info(
                &format!("skipping Install RustDemangler stage{} ({})",
                         self.compiler.stage, self.target),
            );
        }
    };
    Analysis, "analysis", Self::should_build(_config), only_hosts: false, {
        // `expect` should be safe, only None with host != build, but this
        // only uses the `build` compiler
        let tarball = builder.ensure(dist::Analysis {
            // Find the actual compiler (handling the full bootstrap option) which
            // produced the save-analysis data because that data isn't copied
            // through the sysroot uplifting.
            compiler: builder.compiler_for(builder.top_stage, builder.config.build, self.target),
            target: self.target
        }).expect("missing analysis");
        install_sh(builder, "analysis", self.compiler.stage, Some(self.target), &tarball);
    };
    Rustc, "src/librustc", true, only_hosts: true, {
        let tarball = builder.ensure(dist::Rustc {
            compiler: builder.compiler(builder.top_stage, self.target),
        });
        install_sh(builder, "rustc", self.compiler.stage, Some(self.target), &tarball);
    };
);

#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Src {
    pub stage: u32,
}

impl Step for Src {
    type Output = ();
    const DEFAULT: bool = true;
    const ONLY_HOSTS: bool = true;

    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
        let config = &run.builder.config;
        let cond = config.extended && config.tools.as_ref().map_or(true, |t| t.contains("src"));
        run.path("src").default_condition(cond)
    }

    fn make_run(run: RunConfig<'_>) {
        run.builder.ensure(Src { stage: run.builder.top_stage });
    }

    fn run(self, builder: &Builder<'_>) {
        let tarball = builder.ensure(dist::Src);
        install_sh(builder, "src", self.stage, None, &tarball);
    }
}
<|endoftext|>


<commit_before>use std::{
    path::{Path, PathBuf},
    process::Command,
};

use build_helper::t;

use crate::builder::Builder;

#[derive(Copy, Clone)]
pub(crate) enum OverlayKind {
    Rust,
    LLVM,
    Cargo,
    Clippy,
    Miri,
    Rustfmt,
    RLS,
    RustAnalyzer,
}

impl OverlayKind {
    fn legal_and_readme(&self) -> &[&str] {
        match self {
            OverlayKind::Rust => &["COPYRIGHT", "LICENSE-APACHE", "LICENSE-MIT", "README.md"],
            OverlayKind::LLVM => {
                &["src/llvm-project/llvm/LICENSE.TXT", "src/llvm-project/llvm/README.txt"]
            }
            OverlayKind::Cargo => &[
                "src/tools/cargo/README.md",
                "src/tools/cargo/LICENSE-MIT",
                "src/tools/cargo/LICENSE-APACHE",
                "src/tools/cargo/LICENSE-THIRD-PARTY",
            ],
            OverlayKind::Clippy => &[
                "src/tools/clippy/README.md",
                "src/tools/clippy/LICENSE-APACHE",
                "src/tools/clippy/LICENSE-MIT",
            ],
            OverlayKind::Miri => &[
                "src/tools/miri/README.md",
                "src/tools/miri/LICENSE-APACHE",
                "src/tools/miri/LICENSE-MIT",
            ],
            OverlayKind::Rustfmt => &[
                "src/tools/rustfmt/README.md",
                "src/tools/rustfmt/LICENSE-APACHE",
                "src/tools/rustfmt/LICENSE-MIT",
            ],
            OverlayKind::RLS => &[
                "src/tools/rls/README.md",
                "src/tools/rls/LICENSE-APACHE",
                "src/tools/rls/LICENSE-MIT",
            ],
            OverlayKind::RustAnalyzer => &[
                "src/tools/rust-analyzer/README.md",
                "src/tools/rust-analyzer/LICENSE-APACHE",
                "src/tools/rust-analyzer/LICENSE-MIT",
            ],
        }
    }

    fn version(&self, builder: &Builder<'_>) -> String {
        match self {
            OverlayKind::Rust => builder.rust_version(),
            OverlayKind::LLVM => builder.rust_version(),
            OverlayKind::Cargo => {
                builder.cargo_info.version(builder, &builder.release_num("cargo"))
            }
            OverlayKind::Clippy => {
                builder.clippy_info.version(builder, &builder.release_num("clippy"))
            }
            OverlayKind::Miri => builder.miri_info.version(builder, &builder.release_num("miri")),
            OverlayKind::Rustfmt => {
                builder.rustfmt_info.version(builder, &builder.release_num("rustfmt"))
            }
            OverlayKind::RLS => builder.rls_info.version(builder, &builder.release_num("rls")),
            OverlayKind::RustAnalyzer => builder
                .rust_analyzer_info
                .version(builder, &builder.release_num("rust-analyzer/crates/rust-analyzer")),
        }
    }
}

pub(crate) struct Tarball<'a> {
    builder: &'a Builder<'a>,

    pkgname: String,
    component: String,
    target: Option<String>,
    product_name: String,
    overlay: OverlayKind,

    temp_dir: PathBuf,
    image_dir: PathBuf,
    overlay_dir: PathBuf,

    include_target_in_component_name: bool,
    is_preview: bool,
    delete_temp_dir: bool,
}

impl<'a> Tarball<'a> {
    pub(crate) fn new(builder: &'a Builder<'a>, component: &str, target: &str) -> Self {
        Self::new_inner(builder, component, Some(target.into()))
    }

    pub(crate) fn new_targetless(builder: &'a Builder<'a>, component: &str) -> Self {
        Self::new_inner(builder, component, None)
    }

    fn new_inner(builder: &'a Builder<'a>, component: &str, target: Option<String>) -> Self {
        let pkgname = crate::dist::pkgname(builder, component);

        let mut temp_dir = builder.out.join("tmp").join("tarball");
        if let Some(target) = &target {
            temp_dir = temp_dir.join(target);
        }
        let _ = std::fs::remove_dir_all(&temp_dir);

        let image_dir = temp_dir.join("image");
        let overlay_dir = temp_dir.join("overlay");

        Self {
            builder,

            pkgname,
            component: component.into(),
            target,
            product_name: "Rust".into(),
            overlay: OverlayKind::Rust,

            temp_dir,
            image_dir,
            overlay_dir,

            include_target_in_component_name: false,
            is_preview: false,
            delete_temp_dir: true,
        }
    }

    pub(crate) fn set_overlay(&mut self, overlay: OverlayKind) {
        self.overlay = overlay;
    }

    pub(crate) fn set_product_name(&mut self, name: &str) {
        self.product_name = name.into();
    }

    pub(crate) fn include_target_in_component_name(&mut self, include: bool) {
        self.include_target_in_component_name = include;
    }

    pub(crate) fn is_preview(&mut self, is: bool) {
        self.is_preview = is;
    }

    pub(crate) fn image_dir(&self) -> &Path {
        t!(std::fs::create_dir_all(&self.image_dir));
        &self.image_dir
    }

    pub(crate) fn add_file(&self, src: impl AsRef<Path>, destdir: impl AsRef<Path>, perms: u32) {
        // create_dir_all fails to create `foo/bar/.`, so when the destination is "." this simply
        // uses the base directory as the destination directory.
        let destdir = if destdir.as_ref() == Path::new(".") {
            self.image_dir.clone()
        } else {
            self.image_dir.join(destdir.as_ref())
        };

        t!(std::fs::create_dir_all(&destdir));
        self.builder.install(src.as_ref(), &destdir, perms);
    }

    pub(crate) fn add_renamed_file(
        &self,
        src: impl AsRef<Path>,
        destdir: impl AsRef<Path>,
        new_name: &str,
    ) {
        let destdir = self.image_dir.join(destdir.as_ref());
        t!(std::fs::create_dir_all(&destdir));
        self.builder.copy(src.as_ref(), &destdir.join(new_name));
    }

    pub(crate) fn add_legal_and_readme_to(&self, destdir: impl AsRef<Path>) {
        for file in self.overlay.legal_and_readme() {
            self.add_file(self.builder.src.join(file), destdir.as_ref(), 0o644);
        }
    }

    pub(crate) fn add_dir(&self, src: impl AsRef<Path>, dest: impl AsRef<Path>) {
        let dest = self.image_dir.join(dest.as_ref());

        t!(std::fs::create_dir_all(&dest));
        self.builder.cp_r(src.as_ref(), &dest);
    }

    pub(crate) fn persist_work_dir(&mut self) -> PathBuf {
        self.delete_temp_dir = false;
        self.temp_dir.clone()
    }

    pub(crate) fn generate(self) -> PathBuf {
        let mut component_name = self.component.clone();
        if self.is_preview {
            component_name.push_str("-preview");
        }
        if self.include_target_in_component_name {
            component_name.push('-');
            component_name.push_str(
                &self
                    .target
                    .as_ref()
                    .expect("include_target_in_component_name used in a targetless tarball"),
            );
        }

        self.run(|this, cmd| {
            cmd.arg("generate")
                .arg("--image-dir")
                .arg(&this.image_dir)
                .arg(format!("--component-name={}", &component_name));
            this.non_bare_args(cmd);
        })
    }

    pub(crate) fn combine(self, tarballs: &[PathBuf]) {
        let mut input_tarballs = tarballs[0].as_os_str().to_os_string();
        for tarball in &tarballs[1..] {
            input_tarballs.push(",");
            input_tarballs.push(tarball);
        }

        self.run(|this, cmd| {
            cmd.arg("combine").arg("--input-tarballs").arg(input_tarballs);
            this.non_bare_args(cmd);
        });
    }

    pub(crate) fn bare(self) -> PathBuf {
        self.run(|this, cmd| {
            cmd.arg("tarball")
                .arg("--input")
                .arg(&this.image_dir)
                .arg("--output")
                .arg(crate::dist::distdir(this.builder).join(this.package_name()));
        })
    }

    fn package_name(&self) -> String {
        if let Some(target) = &self.target {
            format!("{}-{}", self.pkgname, target)
        } else {
            self.pkgname.clone()
        }
    }

    fn non_bare_args(&self, cmd: &mut Command) {
        cmd.arg("--rel-manifest-dir=rustlib")
            .arg("--legacy-manifest-dirs=rustlib,cargo")
            .arg(format!("--product-name={}", self.product_name))
            .arg(format!("--success-message={} installed.", self.component))
            .arg(format!("--package-name={}", self.package_name()))
            .arg("--non-installed-overlay")
            .arg(&self.overlay_dir)
            .arg("--output-dir")
            .arg(crate::dist::distdir(self.builder));
    }

    fn run(self, build_cli: impl FnOnce(&Tarball<'a>, &mut Command)) -> PathBuf {
        t!(std::fs::create_dir_all(&self.overlay_dir));
        self.builder.create(&self.overlay_dir.join("version"), &self.overlay.version(self.builder));
        if let Some(sha) = self.builder.rust_sha() {
            self.builder.create(&self.overlay_dir.join("git-commit-hash"), &sha);
        }
        for file in self.overlay.legal_and_readme() {
            self.builder.install(&self.builder.src.join(file), &self.overlay_dir, 0o644);
        }

        let mut cmd = self.builder.tool_cmd(crate::tool::Tool::RustInstaller);

        let package_name = self.package_name();
        self.builder.info(&format!("Dist {}", package_name));
        let _time = crate::util::timeit(self.builder);

        build_cli(&self, &mut cmd);
        cmd.arg("--work-dir").arg(&self.temp_dir);
        self.builder.run(&mut cmd);
        if self.delete_temp_dir {
            t!(std::fs::remove_dir_all(&self.temp_dir));
        }

        crate::dist::distdir(self.builder).join(format!("{}.tar.gz", package_name))
    }
}
<commit_msg>bootstrap: put the component name in the tarball temp dir path<commit_after>use std::{
    path::{Path, PathBuf},
    process::Command,
};

use build_helper::t;

use crate::builder::Builder;

#[derive(Copy, Clone)]
pub(crate) enum OverlayKind {
    Rust,
    LLVM,
    Cargo,
    Clippy,
    Miri,
    Rustfmt,
    RLS,
    RustAnalyzer,
}

impl OverlayKind {
    fn legal_and_readme(&self) -> &[&str] {
        match self {
            OverlayKind::Rust => &["COPYRIGHT", "LICENSE-APACHE", "LICENSE-MIT", "README.md"],
            OverlayKind::LLVM => {
                &["src/llvm-project/llvm/LICENSE.TXT", "src/llvm-project/llvm/README.txt"]
            }
            OverlayKind::Cargo => &[
                "src/tools/cargo/README.md",
                "src/tools/cargo/LICENSE-MIT",
                "src/tools/cargo/LICENSE-APACHE",
                "src/tools/cargo/LICENSE-THIRD-PARTY",
            ],
            OverlayKind::Clippy => &[
                "src/tools/clippy/README.md",
                "src/tools/clippy/LICENSE-APACHE",
                "src/tools/clippy/LICENSE-MIT",
            ],
            OverlayKind::Miri => &[
                "src/tools/miri/README.md",
                "src/tools/miri/LICENSE-APACHE",
                "src/tools/miri/LICENSE-MIT",
            ],
            OverlayKind::Rustfmt => &[
                "src/tools/rustfmt/README.md",
                "src/tools/rustfmt/LICENSE-APACHE",
                "src/tools/rustfmt/LICENSE-MIT",
            ],
            OverlayKind::RLS => &[
                "src/tools/rls/README.md",
                "src/tools/rls/LICENSE-APACHE",
                "src/tools/rls/LICENSE-MIT",
            ],
            OverlayKind::RustAnalyzer => &[
                "src/tools/rust-analyzer/README.md",
                "src/tools/rust-analyzer/LICENSE-APACHE",
                "src/tools/rust-analyzer/LICENSE-MIT",
            ],
        }
    }

    fn version(&self, builder: &Builder<'_>) -> String {
        match self {
            OverlayKind::Rust => builder.rust_version(),
            OverlayKind::LLVM => builder.rust_version(),
            OverlayKind::Cargo => {
                builder.cargo_info.version(builder, &builder.release_num("cargo"))
            }
            OverlayKind::Clippy => {
                builder.clippy_info.version(builder, &builder.release_num("clippy"))
            }
            OverlayKind::Miri => builder.miri_info.version(builder, &builder.release_num("miri")),
            OverlayKind::Rustfmt => {
                builder.rustfmt_info.version(builder, &builder.release_num("rustfmt"))
            }
            OverlayKind::RLS => builder.rls_info.version(builder, &builder.release_num("rls")),
            OverlayKind::RustAnalyzer => builder
                .rust_analyzer_info
                .version(builder, &builder.release_num("rust-analyzer/crates/rust-analyzer")),
        }
    }
}

pub(crate) struct Tarball<'a> {
    builder: &'a Builder<'a>,

    pkgname: String,
    component: String,
    target: Option<String>,
    product_name: String,
    overlay: OverlayKind,

    temp_dir: PathBuf,
    image_dir: PathBuf,
    overlay_dir: PathBuf,

    include_target_in_component_name: bool,
    is_preview: bool,
    delete_temp_dir: bool,
}

impl<'a> Tarball<'a> {
    pub(crate) fn new(builder: &'a Builder<'a>, component: &str, target: &str) -> Self {
        Self::new_inner(builder, component, Some(target.into()))
    }

    pub(crate) fn new_targetless(builder: &'a Builder<'a>, component: &str) -> Self {
        Self::new_inner(builder, component, None)
    }

    fn new_inner(builder: &'a Builder<'a>, component: &str, target: Option<String>) -> Self {
        let pkgname = crate::dist::pkgname(builder, component);

        let mut temp_dir = builder.out.join("tmp").join("tarball").join(component);
        if let Some(target) = &target {
            temp_dir = temp_dir.join(target);
        }
        let _ = std::fs::remove_dir_all(&temp_dir);

        let image_dir = temp_dir.join("image");
        let overlay_dir = temp_dir.join("overlay");

        Self {
            builder,

            pkgname,
            component: component.into(),
            target,
            product_name: "Rust".into(),
            overlay: OverlayKind::Rust,

            temp_dir,
            image_dir,
            overlay_dir,

            include_target_in_component_name: false,
            is_preview: false,
            delete_temp_dir: true,
        }
    }

    pub(crate) fn set_overlay(&mut self, overlay: OverlayKind) {
        self.overlay = overlay;
    }

    pub(crate) fn set_product_name(&mut self, name: &str) {
        self.product_name = name.into();
    }

    pub(crate) fn include_target_in_component_name(&mut self, include: bool) {
        self.include_target_in_component_name = include;
    }

    pub(crate) fn is_preview(&mut self, is: bool) {
        self.is_preview = is;
    }

    pub(crate) fn image_dir(&self) -> &Path {
        t!(std::fs::create_dir_all(&self.image_dir));
        &self.image_dir
    }

    pub(crate) fn add_file(&self, src: impl AsRef<Path>, destdir: impl AsRef<Path>, perms: u32) {
        // create_dir_all fails to create `foo/bar/.`, so when the destination is "." this simply
        // uses the base directory as the destination directory.
        let destdir = if destdir.as_ref() == Path::new(".") {
            self.image_dir.clone()
        } else {
            self.image_dir.join(destdir.as_ref())
        };

        t!(std::fs::create_dir_all(&destdir));
        self.builder.install(src.as_ref(), &destdir, perms);
    }

    pub(crate) fn add_renamed_file(
        &self,
        src: impl AsRef<Path>,
        destdir: impl AsRef<Path>,
        new_name: &str,
    ) {
        let destdir = self.image_dir.join(destdir.as_ref());
        t!(std::fs::create_dir_all(&destdir));
        self.builder.copy(src.as_ref(), &destdir.join(new_name));
    }

    pub(crate) fn add_legal_and_readme_to(&self, destdir: impl AsRef<Path>) {
        for file in self.overlay.legal_and_readme() {
            self.add_file(self.builder.src.join(file), destdir.as_ref(), 0o644);
        }
    }

    pub(crate) fn add_dir(&self, src: impl AsRef<Path>, dest: impl AsRef<Path>) {
        let dest = self.image_dir.join(dest.as_ref());

        t!(std::fs::create_dir_all(&dest));
        self.builder.cp_r(src.as_ref(), &dest);
    }

    pub(crate) fn persist_work_dir(&mut self) -> PathBuf {
        self.delete_temp_dir = false;
        self.temp_dir.clone()
    }

    pub(crate) fn generate(self) -> PathBuf {
        let mut component_name = self.component.clone();
        if self.is_preview {
            component_name.push_str("-preview");
        }
        if self.include_target_in_component_name {
            component_name.push('-');
            component_name.push_str(
                &self
                    .target
                    .as_ref()
                    .expect("include_target_in_component_name used in a targetless tarball"),
            );
        }

        self.run(|this, cmd| {
            cmd.arg("generate")
                .arg("--image-dir")
                .arg(&this.image_dir)
                .arg(format!("--component-name={}", &component_name));
            this.non_bare_args(cmd);
        })
    }

    pub(crate) fn combine(self, tarballs: &[PathBuf]) {
        let mut input_tarballs = tarballs[0].as_os_str().to_os_string();
        for tarball in &tarballs[1..] {
            input_tarballs.push(",");
            input_tarballs.push(tarball);
        }

        self.run(|this, cmd| {
            cmd.arg("combine").arg("--input-tarballs").arg(input_tarballs);
            this.non_bare_args(cmd);
        });
    }

    pub(crate) fn bare(self) -> PathBuf {
        self.run(|this, cmd| {
            cmd.arg("tarball")
                .arg("--input")
                .arg(&this.image_dir)
                .arg("--output")
                .arg(crate::dist::distdir(this.builder).join(this.package_name()));
        })
    }

    fn package_name(&self) -> String {
        if let Some(target) = &self.target {
            format!("{}-{}", self.pkgname, target)
        } else {
            self.pkgname.clone()
        }
    }

    fn non_bare_args(&self, cmd: &mut Command) {
        cmd.arg("--rel-manifest-dir=rustlib")
            .arg("--legacy-manifest-dirs=rustlib,cargo")
            .arg(format!("--product-name={}", self.product_name))
            .arg(format!("--success-message={} installed.", self.component))
            .arg(format!("--package-name={}", self.package_name()))
            .arg("--non-installed-overlay")
            .arg(&self.overlay_dir)
            .arg("--output-dir")
            .arg(crate::dist::distdir(self.builder));
    }

    fn run(self, build_cli: impl FnOnce(&Tarball<'a>, &mut Command)) -> PathBuf {
        t!(std::fs::create_dir_all(&self.overlay_dir));
        self.builder.create(&self.overlay_dir.join("version"), &self.overlay.version(self.builder));
        if let Some(sha) = self.builder.rust_sha() {
            self.builder.create(&self.overlay_dir.join("git-commit-hash"), &sha);
        }
        for file in self.overlay.legal_and_readme() {
            self.builder.install(&self.builder.src.join(file), &self.overlay_dir, 0o644);
        }

        let mut cmd = self.builder.tool_cmd(crate::tool::Tool::RustInstaller);

        let package_name = self.package_name();
        self.builder.info(&format!("Dist {}", package_name));
        let _time = crate::util::timeit(self.builder);

        build_cli(&self, &mut cmd);
        cmd.arg("--work-dir").arg(&self.temp_dir);
        self.builder.run(&mut cmd);
        if self.delete_temp_dir {
            t!(std::fs::remove_dir_all(&self.temp_dir));
        }

        crate::dist::distdir(self.builder).join(format!("{}.tar.gz", package_name))
    }
}
<|endoftext|>