diff --git a/task-maker-format/src/ioi/format/italian_yaml/cases_gen.rs b/task-maker-format/src/ioi/format/italian_yaml/cases_gen.rs index 0a08ae39a..fbd0a21e4 100644 --- a/task-maker-format/src/ioi/format/italian_yaml/cases_gen.rs +++ b/task-maker-format/src/ioi/format/italian_yaml/cases_gen.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use std::fmt::Display; use std::fmt::Formatter; use std::fmt::Write; -use std::io::Read; +use std::fs; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::Arc; @@ -146,7 +146,7 @@ where .context("Invalid gen/cases.gen path")? .parent() .context("Invalid gen/cases.gen path")?; - let content = std::fs::read_to_string(path) + let content = fs::read_to_string(path) .with_context(|| format!("Failed to read {}", path.display()))?; let mut file = parser::CasesGenParser::parse(parser::Rule::file, &content) .context("Cannot parse cases.gen")?; @@ -209,7 +209,7 @@ where /// Write an auto-generated version of the gen/GEN file inside the task directory. pub(crate) fn write_gen_gen(&self) -> Result<(), Error> { let dest = self.task_dir.join("gen/GEN"); - if dest.exists() && !is_gen_gen_deletable(&dest)? { + if dest.exists() && !is_tm_deletable(&dest)? { warn!( "The gen/GEN file does not contain {}. Won't overwrite", TM_ALLOW_DELETE_COOKIE @@ -256,7 +256,7 @@ where } } } - std::fs::write(self.task_dir.join("gen/GEN"), gen).context("Failed to write gen/GEN")?; + fs::write(self.task_dir.join("gen/GEN"), gen).context("Failed to write gen/GEN")?; Ok(()) } @@ -668,22 +668,12 @@ where } } -/// Check if the gen/GEN file is deletable, i.e. it exists and it is autogenerated. -pub(crate) fn is_gen_gen_deletable(path: &Path) -> Result { - if !path.exists() { - return Ok(false); - } - let mut buffer = vec![]; - let mut file = std::fs::File::open(path) - .with_context(|| format!("Cannot open file {}", path.display()))?; - file.read_to_end(&mut buffer) - .context("Cannot read gen/GEN file")?; +/// Check if the file is deletable, i.e. it contains the TM_ALLOW_DELETE_COOKIE +/// Assumes the file exists. +pub(crate) fn is_tm_deletable(path: &Path) -> Result { + let buffer = fs::read(path).with_context(|| format!("Cannot read file {}", path.display()))?; let content = String::from_utf8_lossy(&buffer); - // if the gen/GEN is present and it does not contain the cookie, skip all the generation - if !content.contains(TM_ALLOW_DELETE_COOKIE) { - return Ok(false); - } - Ok(true) + Ok(content.contains(TM_ALLOW_DELETE_COOKIE)) } impl ConstraintOperator { diff --git a/task-maker-format/src/ioi/format/italian_yaml/mod.rs b/task-maker-format/src/ioi/format/italian_yaml/mod.rs index 7d2a2192f..d2308ec65 100644 --- a/task-maker-format/src/ioi/format/italian_yaml/mod.rs +++ b/task-maker-format/src/ioi/format/italian_yaml/mod.rs @@ -257,7 +257,7 @@ use std::collections::HashMap; use std::fs::File; -use std::io::BufWriter; +use std::io::{BufWriter, Write}; use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -267,7 +267,7 @@ use serde::{Deserialize, Serialize, Serializer}; use unic::normal::StrNormalForm; use unic::ucd::category::GeneralCategory; -pub(crate) use cases_gen::{is_gen_gen_deletable, TM_ALLOW_DELETE_COOKIE}; +pub(crate) use cases_gen::{is_tm_deletable, TM_ALLOW_DELETE_COOKIE}; use task_maker_lang::GraderMap; use crate::ioi::sanity_checks::get_sanity_checks; @@ -500,21 +500,25 @@ pub fn parse_task>( ) -> Result { let task_dir = task_dir.as_ref(); + let task_yaml_path = task_dir.join("task.yaml"); + let task_yaml_orig_path = task_dir.join("task.yaml.orig"); let task_yaml_overwrite: bool; let mut yaml: TaskYAML; - if task_dir.join("task.yaml.orig").exists() { + if task_yaml_orig_path.exists() { task_yaml_overwrite = true; - let path = task_dir.join(task_dir.join("task.yaml.orig")); - let file = File::open(&path) - .with_context(|| format!("Cannot open task.yaml.orig from {}", path.display()))?; + let file = File::open(&task_yaml_orig_path).with_context(|| { + format!( + "Cannot open task.yaml.orig from {}", + task_yaml_orig_path.display() + ) + })?; let yaml_orig: TaskYAMLOrig = serde_yaml::from_reader(file).context("Failed to deserialize task.yaml.orig")?; yaml = yaml_orig.into_task_yaml(task_dir); - } else if task_dir.join("task.yaml").exists() { + } else if task_yaml_path.exists() { task_yaml_overwrite = false; - let path = task_dir.join(task_dir.join("task.yaml")); - let file = File::open(&path) - .with_context(|| format!("Cannot open task.yaml from {}", path.display()))?; + let file = File::open(&task_yaml_path) + .with_context(|| format!("Cannot open task.yaml from {}", task_yaml_path.display()))?; yaml = serde_yaml::from_reader(file).context("Failed to deserialize task.yaml")?; } else { bail!("No task.yaml found in {}", task_dir.display()); @@ -630,27 +634,37 @@ pub fn parse_task>( yaml.score_type = Some(testcase_score_aggregator); if task_yaml_overwrite { - if !eval_config.dry_run { - yaml.score_type_parameters = Some( - subtasks - .iter() - .sorted_by_key(|(id, _)| *id) - .map(|(_, st)| { - let testcases = st - .testcases - .iter() - .map(|tc_num| format!("{tc_num:03}")) - .join("|"); - (st.max_score, testcases) - }) - .collect(), + if !task_yaml_path.exists() || is_tm_deletable(&task_yaml_path)? { + if !eval_config.dry_run { + yaml.score_type_parameters = Some( + subtasks + .iter() + .sorted_by_key(|(id, _)| *id) + .map(|(_, st)| { + let testcases = st + .testcases + .iter() + .map(|tc_num| format!("{tc_num:03}")) + .join("|"); + (st.max_score, testcases) + }) + .collect(), + ); + + let file = File::create(&task_yaml_path).with_context(|| { + format!("Cannot open task.yaml from {}", task_yaml_path.display()) + })?; + let mut buf_file = BufWriter::new(file); + writeln!(buf_file, "# Generated by task-maker. Do not edit!")?; + writeln!(buf_file, "# {}", TM_ALLOW_DELETE_COOKIE)?; + writeln!(buf_file, "# Removing or changing the line above will prevent task-maker from touching this file again.")?; + serde_yaml::to_writer(buf_file, &yaml).context("Failed to serialize task.yaml")?; + } + } else { + warn!( + "The task.yaml file does not contain {}. Won't overwrite", + TM_ALLOW_DELETE_COOKIE ); - - let path = task_dir.join("task.yaml"); - let file = File::create(&path) - .with_context(|| format!("Cannot open task.yaml from {}", path.display()))?; - serde_yaml::to_writer(BufWriter::new(file), &yaml) - .context("Failed to serialize task.yaml")?; } } else if subtasks.values().any(|st| !st.dependencies.is_empty()) { bail!("Use task.yaml.orig to use subtask dependencies"); diff --git a/task-maker-format/src/ioi/mod.rs b/task-maker-format/src/ioi/mod.rs index 8ad657b62..ec7489e12 100644 --- a/task-maker-format/src/ioi/mod.rs +++ b/task-maker-format/src/ioi/mod.rs @@ -38,7 +38,7 @@ use task_maker_lang::GraderMap; pub use ui_state::*; use crate::ioi::format::italian_yaml::TM_ALLOW_DELETE_COOKIE; -use crate::ioi::italian_yaml::is_gen_gen_deletable; +use crate::ioi::italian_yaml::is_tm_deletable; use crate::sanity_checks::SanityChecks; use crate::solution::SolutionInfo; use crate::ui::*; @@ -481,7 +481,7 @@ impl IOITask { let gen_gen_path = self.path.join("gen/GEN"); let cases_gen_path = self.path.join("gen/cases.gen"); if cases_gen_path.exists() && gen_gen_path.exists() { - if is_gen_gen_deletable(&gen_gen_path)? { + if is_tm_deletable(&gen_gen_path)? { info!("Removing {}", gen_gen_path.display()); std::fs::remove_file(&gen_gen_path).with_context(|| { format!("Failed to remove gen/GEN at {}", gen_gen_path.display()) @@ -493,6 +493,22 @@ impl IOITask { ); } } + // remove task.yaml if there is task.yaml.orig + let task_yaml_path = self.path.join("task.yaml"); + let task_yaml_orig_path = self.path.join("task.yaml.orig"); + if task_yaml_orig_path.exists() && task_yaml_path.exists() { + if is_tm_deletable(&task_yaml_path)? { + info!("Removing {}", task_yaml_path.display()); + std::fs::remove_file(&task_yaml_path).with_context(|| { + format!("Failed to remove task.yaml at {}", task_yaml_path.display()) + })?; + } else { + warn!( + "Won't remove task.yaml since it doesn't contain {}", + TM_ALLOW_DELETE_COOKIE + ); + } + } Ok(()) }