diff --git a/README.md b/README.md index 1d69bdfce..66081fc82 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,43 @@ If you would like to use an unreleased version of vcf-validator, you can clone t If you plan to make changes, make sure to check the [docs/developer-guide.md](docs/developer-guide.md). +### Nix + +[Nix](https://nixos.org/) is a package manager that provides reproducible environments across Linux, macOS, and Windows (via WSL2). + +#### Running without installation + +You can run vcf-validator directly from GitHub without cloning or installing dependencies: + +```bash +# Run the validator +nix run github:EBIvariation/vcf-validator -- --help + +# Run the assembly checker +nix run github:EBIvariation/vcf-validator#vcf_assembly_checker -- --help +``` + +#### Development + +For local development with all dependencies pre-installed: + +```bash +# Clone and enter development shell +git clone https://github.com/EBIvariation/vcf-validator.git +cd vcf-validator +nix develop + +# Build locally +mkdir build && cd build +cmake -DSTATIC_BUILD=OFF .. +make + +# Or build with Nix +nix build +``` + +The development shell includes CMake, Boost, and C++ linters (clang-tidy, cppcheck) ready to use. + ### Linux The build has been tested on the following compilers: diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..ed3502eac --- /dev/null +++ b/flake.nix @@ -0,0 +1,101 @@ +{ + description = "VCF Validator - Validation suite for Variant Call Format (VCF) files"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + { + packages.default = pkgs.stdenv.mkDerivation rec { + pname = "vcf-validator"; + version = "0.10.2"; + + src = ./.; + + nativeBuildInputs = with pkgs; [ + cmake + pkg-config + ]; + + buildInputs = with pkgs; [ + boost + curl + openssl + zlib + bzip2 + ]; + + cmakeFlags = [ + "-DSTATIC_BUILD=OFF" + "-DCMAKE_BUILD_TYPE=Release" + ]; + + enableParallelBuilding = true; + + installPhase = '' + runHook preInstall + + mkdir -p $out/bin + cp bin/vcf_validator $out/bin/ + cp bin/vcf_assembly_checker $out/bin/ + + runHook postInstall + ''; + + doCheck = true; + checkPhase = '' + runHook preCheck + ctest --output-on-failure + runHook postCheck + ''; + + meta = with pkgs.lib; { + description = "Validation suite for Variant Call Format (VCF) files"; + homepage = "https://github.com/EBIvariation/vcf-validator"; + license = licenses.asl20; + maintainers = [ ]; + platforms = platforms.unix; + }; + }; + + apps.default = { + type = "app"; + program = "${self.packages.${system}.default}/bin/vcf_validator"; + }; + + apps.vcf_validator = { + type = "app"; + program = "${self.packages.${system}.default}/bin/vcf_validator"; + }; + + apps.vcf_assembly_checker = { + type = "app"; + program = "${self.packages.${system}.default}/bin/vcf_assembly_checker"; + }; + + devShells.default = pkgs.mkShell { + inputsFrom = [ self.packages.${system}.default ]; + packages = with pkgs; [ + # Debugging tools + gdb + valgrind + + # Linters and static analysis + clang-tools # clang-tidy, clang-format + cppcheck # C++ static analyzer + include-what-you-use # Include optimization + cpplint # Google's C++ style checker + + # Build tools + bear # Generates compile_commands.json for clang tools + ninja # Faster builds + ]; + }; + }); +} \ No newline at end of file diff --git a/src/fasta/faidx.cpp b/src/fasta/faidx.cpp index 876e3639b..db20f7d8f 100644 --- a/src/fasta/faidx.cpp +++ b/src/fasta/faidx.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include #include diff --git a/src/vcf/assembly_checker.cpp b/src/vcf/assembly_checker.cpp index c97bfbaf4..4b4cfe032 100644 --- a/src/vcf/assembly_checker.cpp +++ b/src/vcf/assembly_checker.cpp @@ -139,7 +139,7 @@ namespace ebi try { fasta->sequence(contig, 0, 1); } - catch(ebi::util::curl::URLRetrievalException ex) { + catch(const ebi::util::curl::URLRetrievalException& ex) { std::string warningMessage = "Could not download sequence for contig/chromosome " + contig + " because: " + ex.what(); write_warning_output(outputs, warningMessage); @@ -250,7 +250,7 @@ namespace ebi try { fasta->sequence(contig_name, 0, 1); // trigger download } - catch(ebi::util::curl::URLRetrievalException ex) { + catch(const ebi::util::curl::URLRetrievalException& ex) { report_missing_chromosome_in_ENA(line_num, ex.what(), record_core, outputs); is_valid = false; continue; diff --git a/src/vcf/record.cpp b/src/vcf/record.cpp index 04a6e62db..d4c16430f 100644 --- a/src/vcf/record.cpp +++ b/src/vcf/record.cpp @@ -261,7 +261,7 @@ namespace ebi try { check_info_field_cardinality(values, key_values[NUMBER]); check_field_type(values, key_values[TYPE]); - } catch (std::shared_ptr ex) { + } catch (const std::shared_ptr& ex) { std::string message = "INFO " + key_values[ID] + " does not match the meta" + ex->message; throw new InfoBodyError{line, message, key_values[ID] + "=" + field.second, ErrorFix::IRRECOVERABLE_VALUE, key_values[ID]}; @@ -280,7 +280,7 @@ namespace ebi } else { check_predefined_tag_info(field.first, values, info_v44); } - } catch (std::shared_ptr ex) { + } catch (const std::shared_ptr& ex) { throw new InfoBodyError{line, "INFO " + ex->message, field.first + "=" + field.second, ErrorFix::IRRECOVERABLE_VALUE, field.first}; } } @@ -383,7 +383,7 @@ namespace ebi try { check_info_field_cardinality(values, get_predefined_number(iterator)); check_field_type(values, get_predefined_type(iterator)); - } catch (std::shared_ptr ex) { + } catch (const std::shared_ptr& ex) { raise(std::make_shared(line, field_key + " does not match the" + ex->message)); } if (get_predefined_type(iterator) == INTEGER) { @@ -402,7 +402,7 @@ namespace ebi long cardinality; check_sample_field_cardinality(values, get_predefined_number(iterator), ploidy, cardinality); check_field_type(values, get_predefined_type(iterator)); - } catch (std::shared_ptr ex) { + } catch (const std::shared_ptr& ex) { raise(std::make_shared(line, field_key + " does not match the" + ex->message, ex->detailed_message)); } @@ -838,7 +838,7 @@ namespace ebi try { check_sample_field_cardinality(values, meta_entry_properties[NUMBER], ploidy, expected_cardinality); check_field_type(values, meta_entry_properties[TYPE]); - } catch (std::shared_ptr ex) { + } catch (const std::shared_ptr& ex) { std::string message = "Sample #" + std::to_string(i + 1) + ", field " + meta_entry_properties[ID] + " does not match the meta" + ex->message; std::string detailed_message = meta_entry_properties[ID] + "=" + subfield + ex->detailed_message; @@ -854,7 +854,7 @@ namespace ebi } else { check_predefined_tag_format(format[j], values, format_v44, ploidy); } - } catch (std::shared_ptr ex) { + } catch (const std::shared_ptr& ex) { throw new SamplesFieldBodyError{line, "Sample #" + std::to_string(i + 1) + ", " + ex->message, format[j] + "=" + subfield, format[j]}; } @@ -1070,7 +1070,7 @@ namespace ebi // ...try to cast to float try { std::stof(value); - } catch (std::out_of_range) { + } catch (const std::out_of_range&) { // It maybe a subnormal number std::stold(value); } diff --git a/src/vcf/store_parse_policy.cpp b/src/vcf/store_parse_policy.cpp index 1651950e8..7ef56bbda 100644 --- a/src/vcf/store_parse_policy.cpp +++ b/src/vcf/store_parse_policy.cpp @@ -212,7 +212,7 @@ namespace ebi try { // Transform the position token into a size_t position = static_cast(std::stoi(m_line_tokens[POS][0])); - } catch (std::invalid_argument ex) { + } catch (const std::invalid_argument&) { throw new PositionBodyError{state.n_lines}; } @@ -221,7 +221,7 @@ namespace ebi if (m_line_tokens[QUAL][0] != MISSING_VALUE) { try { quality = std::stof(m_line_tokens[QUAL][0]); - } catch (std::invalid_argument ex) { + } catch (const std::invalid_argument&) { throw new QualityBodyError{state.n_lines}; } } @@ -271,7 +271,7 @@ namespace ebi { try { return m_line_tokens.at(column); - } catch (std::out_of_range) { + } catch (const std::out_of_range&) { return {}; } }