[med-svn] [Git][med-team/kissplice][master] 7 commits: New upstream version 2.6.1
Andreas Tille (@tille)
gitlab at salsa.debian.org
Thu Jun 30 15:45:12 BST 2022
Andreas Tille pushed to branch master at Debian Med / kissplice
Commits:
f824c786 by Andreas Tille at 2022-04-06T12:36:39+02:00
New upstream version 2.6.1
- - - - -
d801d0ad by Andreas Tille at 2022-06-30T16:12:12+02:00
New upstream version 2.6.2
- - - - -
f0b6bdd8 by Andreas Tille at 2022-06-30T16:12:12+02:00
routine-update: New upstream version
- - - - -
b1a398e5 by Andreas Tille at 2022-06-30T16:12:30+02:00
Update upstream source from tag 'upstream/2.6.2'
Update to upstream version '2.6.2'
with Debian dir 311a418d9ffae78defecc0657f7106e11a534613
- - - - -
5b73eaca by Andreas Tille at 2022-06-30T16:12:30+02:00
routine-update: Standards-Version: 4.6.1
- - - - -
5ad99901 by Andreas Tille at 2022-06-30T16:27:11+02:00
Refresh patch
- - - - -
9a0a1a18 by Andreas Tille at 2022-06-30T16:28:08+02:00
Upload to unstable
- - - - -
24 changed files:
- CMakeLists.txt
- ChangeLog
- README.md
- debian/changelog
- debian/control
- debian/patches/tests-2to3.patch
- doc/CMakeLists.txt
- doc/quiet.sh
- + doc/rm_pdf_id.sh
- doc/user_guide.in.tex
- kissplice.in.py
- man/CMakeLists.txt
- man/kissplice.in.1
- tests/integration_tests/CMakeLists.txt
- tests/integration_tests/data/graph_HBM75brain_100000_HBM75liver_100000_k25.abundance0
- tests/integration_tests/data/graph_HBM75brain_100000_HBM75liver_100000_k25.edges0
- tests/integration_tests/data/graph_HBM75brain_100000_HBM75liver_100000_k25.nodes0
- tests/integration_tests/kisspliceDBGTest.py
- tests/integration_tests/kisspliceGraphAndSequenceTest.py
- tests/integration_tests/kisspliceTwoSequencesTest.py
- thirdparty/kissreads/include/coherence_algorithm.h
- thirdparty/kissreads/src/coherent_algorithm.cpp
- thirdparty/kissreads/src/extension_algorithm.cpp
- − utils/cmake/cmake_uninstall.cmake.in
Changes:
=====================================
CMakeLists.txt
=====================================
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.1)
project(
kissplice
- VERSION 2.6.1 # Definition which is propagated through PROJECT_VERSION
+ VERSION 2.6.2 # Definition which is propagated through PROJECT_VERSION
LANGUAGES CXX
)
@@ -23,6 +23,9 @@ add_compile_options(-Wall)
# Profiling with gprof requires -pg which is better to add manually with -DCMAKE_(C|CXX)_FLAGS=-pg
# `perf` is a more modern replacement anyway and does not require special compilation.
+# Date for manpage and pdf doc
+string(TIMESTAMP CONFIGURE_DATE "%Y-%m-%d" UTC)
+
###############################################################################
# structure and paths
# kissplice is composed of:
@@ -97,13 +100,3 @@ add_subdirectory(doc) # user guide
enable_testing()
add_subdirectory(tests)
-# ============================================================================
-# Add custom uninstall target
-# ============================================================================
-configure_file(
- "${CMAKE_CURRENT_SOURCE_DIR}/utils/cmake/cmake_uninstall.cmake.in"
- "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
- IMMEDIATE @ONLY)
-
-add_custom_target(uninstall
- COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
=====================================
ChangeLog
=====================================
@@ -1,3 +1,7 @@
+UNRELEASED
+ Improvements:
+ - manpage and user_guide.pdf build process should now be reproducible
+
2022-04-05 Francois Gindraud <francois.gindraud at inria.fr>
* 2.6.0 -> 2.6.1
Bug fix:
=====================================
README.md
=====================================
@@ -4,14 +4,19 @@ Main page: http://kissplice.prabi.fr/
[User guide](https://gitlab.inria.fr/erable/kissplice/-/jobs/artifacts/master/file/prefix/share/doc/kissplice/user_guide.pdf?job=check_install_and_user_guide) from latest build.
-# Docker
+# Pre-packaged versions
+## Docker
You can find the latest version of KisSplice, KisSplice2RefGenome and KissDE [on Docker Hub](https://hub.docker.com/repository/docker/dwishsan/kissplice-pipeline).
We also propose a [stand-alone Docker image for the KissDE Shiny App](https://hub.docker.com/repository/docker/dwishsan/kde-shiny) for KisSplice results exploration.
-# Building:
+## Linux
+A package for kissplice is available on Debian / Ubuntu, but it may lag the current version.
+# Build from source
+
+## Dependencies
Required:
- cmake >= 3.1
- C/C++11 compiler toolchain (binutils, gcc or clang)
@@ -21,9 +26,12 @@ Recommended but optional:
- [bcalm](https://github.com/GATB/bcalm) >= v2.2.2 ; as a fallback a locally compiled version of bcalm can be used by passing `-DUSE_BUNDLED_BCALM=TRUE` to cmake. A debian/ubuntu package is available.
- latex toolchain with standard packages to build the user_guide ; this can be disabled by passing `-DUSER_GUIDE=OFF` to cmake
+## Build
+Download from the latest [release](https://gitlab.inria.fr/erable/kissplice/-/releases) and uncompress it.
+You can also clone the git repository, but the latest code may be broken..
+Then compile using :
```
-git clone https://gitlab.inria.fr/erable/kissplice
-cd kissplice/
+cd kissplice/ # Replace by the directory where the release tar.gz was uncompressed, or where the git was cloned
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
@@ -39,7 +47,7 @@ make
make install
```
-# Running on the sample test:
+# Running on the sample test
```
cd kissplice/build
./bin/kissplice -r ../sample_example/mock1.fq -r ../sample_example/mock2.fq -r ../sample_example/virus1.fq -r ../sample_example/virus2.fq
=====================================
debian/changelog
=====================================
@@ -1,3 +1,10 @@
+kissplice (2.6.2-1) unstable; urgency=medium
+
+ * New upstream version
+ * Standards-Version: 4.6.1 (routine-update)
+
+ -- Andreas Tille <tille at debian.org> Thu, 30 Jun 2022 16:27:19 +0200
+
kissplice (2.6.1-1) unstable; urgency=medium
[ Andreas Tille ]
=====================================
debian/control
=====================================
@@ -6,13 +6,13 @@ Section: science
Priority: optional
Build-Depends: debhelper-compat (= 13),
dh-python,
- cmake (>= 3.1),
+ cmake,
python3-dev,
libpython3-stdlib,
zlib1g-dev,
texlive-latex-recommended,
- bcalm (>= 2.2.2) <!nocheck>
-Standards-Version: 4.6.0
+ bcalm <!nocheck>
+Standards-Version: 4.6.1
Vcs-Browser: https://salsa.debian.org/med-team/kissplice
Vcs-Git: https://salsa.debian.org/med-team/kissplice.git
Homepage: http://kissplice.prabi.fr/
@@ -23,7 +23,7 @@ Architecture: any-amd64 any-arm64 any-mips64 any-mips64el any-ia64 any-ppc64 any
Depends: ${shlibs:Depends},
${misc:Depends},
${python3:Depends},
- bcalm (>= 2.2.2)
+ bcalm
Description: Detection of various kinds of polymorphisms in RNA-seq data
KisSplice is a piece of software that enables the analysis of RNA-seq data
with or without a reference genome. It is an exact local transcriptome
=====================================
debian/patches/tests-2to3.patch
=====================================
@@ -4,30 +4,24 @@ Last-Update: Sun, 12 Apr 2020 18:40:00 +0200
Forwarded: in https://bugs.debian.org/936798 to
LACROIX VINCENT <Vincent.Lacroix at univ-lyon1.fr>, David Parsons <david.parsons at inria.fr>
-Index: kissplice/tests/functional_tests/ProcessLauncher.py
-===================================================================
---- kissplice.orig/tests/functional_tests/ProcessLauncher.py
-+++ kissplice/tests/functional_tests/ProcessLauncher.py
+--- a/tests/functional_tests/ProcessLauncher.py
++++ b/tests/functional_tests/ProcessLauncher.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/python3
from sys import exit
import shutil
import shlex
-Index: kissplice/tests/functional_tests/ksBubbleEnumerationTest.py
-===================================================================
---- kissplice.orig/tests/functional_tests/ksBubbleEnumerationTest.py
-+++ kissplice/tests/functional_tests/ksBubbleEnumerationTest.py
+--- a/tests/functional_tests/ksBubbleEnumerationTest.py
++++ b/tests/functional_tests/ksBubbleEnumerationTest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/python3
import re
import os
import fnmatch
-Index: kissplice/tests/functional_tests/ksErrorRemovalTest.py
-===================================================================
---- kissplice.orig/tests/functional_tests/ksErrorRemovalTest.py
-+++ kissplice/tests/functional_tests/ksErrorRemovalTest.py
+--- a/tests/functional_tests/ksErrorRemovalTest.py
++++ b/tests/functional_tests/ksErrorRemovalTest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/python3
@@ -41,20 +35,16 @@ Index: kissplice/tests/functional_tests/ksErrorRemovalTest.py
- print("ks_error_removal: test FAILED")
\ No newline at end of file
+ print("ks_error_removal: test FAILED")
-Index: kissplice/tests/functional_tests/ksRunModulesTest.py
-===================================================================
---- kissplice.orig/tests/functional_tests/ksRunModulesTest.py
-+++ kissplice/tests/functional_tests/ksRunModulesTest.py
+--- a/tests/functional_tests/ksRunModulesTest.py
++++ b/tests/functional_tests/ksRunModulesTest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/python3
import re
import fnmatch
import os
-Index: kissplice/tests/integration_tests/ProcessLauncher.py
-===================================================================
---- kissplice.orig/tests/integration_tests/ProcessLauncher.py
-+++ kissplice/tests/integration_tests/ProcessLauncher.py
+--- a/tests/integration_tests/ProcessLauncher.py
++++ b/tests/integration_tests/ProcessLauncher.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/python3
@@ -68,47 +58,24 @@ Index: kissplice/tests/integration_tests/ProcessLauncher.py
- return res[0]
\ No newline at end of file
+ return res[0]
-Index: kissplice/tests/integration_tests/kisspliceDBGTest.py
-===================================================================
---- kissplice.orig/tests/integration_tests/kisspliceDBGTest.py
-+++ kissplice/tests/integration_tests/kisspliceDBGTest.py
+--- a/tests/integration_tests/kisspliceGraphAndSequenceTest.py
++++ b/tests/integration_tests/kisspliceGraphAndSequenceTest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/python3
import re
from sys import argv
from os.path import dirname, abspath
-@@ -51,4 +51,4 @@ else:
- print("kisspliceDBGTest.py: test FAILED")
-
- command_line_rm = "rm -r "+TEST_INSTDIR +"/results"
--ProcessLauncher.run(command_line_rm)
-\ No newline at end of file
-+ProcessLauncher.run(command_line_rm)
-Index: kissplice/tests/integration_tests/kisspliceGraphAndSequenceTest.py
-===================================================================
---- kissplice.orig/tests/integration_tests/kisspliceGraphAndSequenceTest.py
-+++ kissplice/tests/integration_tests/kisspliceGraphAndSequenceTest.py
-@@ -1,4 +1,4 @@
--#!/usr/bin/env python3
-+#!/usr/bin/python3
- import re
- from sys import argv
- from os.path import dirname, abspath
-Index: kissplice/tests/integration_tests/kisspliceGraphTest.py
-===================================================================
---- kissplice.orig/tests/integration_tests/kisspliceGraphTest.py
-+++ kissplice/tests/integration_tests/kisspliceGraphTest.py
+--- a/tests/integration_tests/kisspliceGraphTest.py
++++ b/tests/integration_tests/kisspliceGraphTest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/python3
import re
from sys import argv
from os.path import dirname, abspath
-Index: kissplice/tests/integration_tests/kisspliceTwoSequencesTest.py
-===================================================================
---- kissplice.orig/tests/integration_tests/kisspliceTwoSequencesTest.py
-+++ kissplice/tests/integration_tests/kisspliceTwoSequencesTest.py
+--- a/tests/integration_tests/kisspliceTwoSequencesTest.py
++++ b/tests/integration_tests/kisspliceTwoSequencesTest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/python3
=====================================
doc/CMakeLists.txt
=====================================
@@ -20,12 +20,13 @@ if (USER_GUIDE)
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/user_guide.pdf"
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/quiet.sh" "${PDFLATEX_COMPILER}" -halt-on-error -interaction=nonstopmode "-output-directory=${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/user_guide.tex"
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/quiet.sh" "${PDFLATEX_COMPILER}" -halt-on-error -interaction=nonstopmode "-output-directory=${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/user_guide.tex"
+ COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/rm_pdf_id.sh" "${CMAKE_CURRENT_BINARY_DIR}/user_guide.pdf"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
MAIN_DEPENDENCY "${CMAKE_CURRENT_BINARY_DIR}/user_guide.tex"
- DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/quiet.sh"
+ DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/quiet.sh" "${CMAKE_CURRENT_SOURCE_DIR}/rm_pdf_id.sh"
)
add_custom_target(doc ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/user_guide.pdf")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/user_guide.pdf" DESTINATION "${CMAKE_INSTALL_DOCDIR}")
else()
message(WARNING "user_guide.pdf will not be built, disabled by USER_GUIDE variable")
-endif()
\ No newline at end of file
+endif()
=====================================
doc/quiet.sh
=====================================
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
-# Execute a command while capturing all outputs if VERBOSE / VERBOSE_LATEX is not requested.
+# Execute a latex command while capturing all outputs if VERBOSE / VERBOSE_LATEX is not requested.
# If the command does not succeed print its outputs for debugging.
# This is intended to quiet latex verbosity in the nice case.
# Compiling with `make VERBOSE_LATEX=1` is useful to debug latex compiling only (VERBOSE would print commands for c/c++ too).
=====================================
doc/rm_pdf_id.sh
=====================================
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+# Remove PDF ID metadata which breaks reproducible builds.
+# This field is not required.
+# Reference : https://wiki.debian.org/ReproducibleBuilds/TimestampsInPDFGeneratedByLaTeX
+for pdf in "$@"; do
+ grep -av '^/ID \[\(<[0-9A-F]\{32\}>\) \1]$' "${pdf}" > "${pdf}.no_id"
+ mv -f "${pdf}.no_id" "${pdf}"
+done
=====================================
doc/user_guide.in.tex
=====================================
@@ -34,6 +34,7 @@ De-novo calling alternative splicing events\\ from RNA-seq data\\
User's guide, version @PROJECT_VERSION@
\vspace{-1cm}
}
+\date{@CONFIGURE_DATE@}
\author{}
\begin{document}
=====================================
kissplice.in.py
=====================================
@@ -72,6 +72,665 @@ logFileName = ""
unfinished_bccs = []
num_snps = {}
+############### NEW GLOBAL VALUES -> REDUNDANCE ###############
+# Maybe switch some of them to parameters?
+ENTROPYMAX=1.8 # If an upper path have less than this entropy, its cycle is deleted
+MAXLENGTHIDIOTIC=200 # Do idiotic strategie for upper paths of the same length and length >= this value
+MAXLENGTHALIGN=1000 # Do not try to align upper paths with a length >= this value (will miss some rare cases redundancy)
+BASEDIFFPATH_UP=2 # Base max difference of length between two upper path for them to be compared (try redundancy removal)
+BASEDIFFPATH_LOW=5 # Base max difference of length between two lower path for them to be compared (try redundancy removal)
+BASEMM_LOW=3 # Base max edit distance (levenshtein) allowed between 2 lower paths
+BASEMM_UP=3 # Base max edit distance (levenshtein) allowed between 2 upper paths
+WILDECARDS=["N","I"]
+# Levenshtein distance parameters
+EXT=1 # Malus for gap extension
+MATCH=0 # Malus for match
+MMATCH=1 # Malus for mismatch
+GAP=1 # Malus for gap openning
+# Entropy
+WLEN=41 # Window length on sequence to compute entropy
+WSLIDE=41 # Right slide of the window on the sequence
+###############################################################
+
+
+############### NEW FUNCTIONS -> ENTROPY ###############
+# Natural log aproximation using ln(x)=lim(x->inf) n(x**(1/n)-1)
+def ln(x):
+ n = 10000.0 # Increase this number for more accurate estimation
+ return n * ((x ** (1/n)) - 1)
+
+LN2=ln(2) # For conversion between natural and base 2 log
+
+def entropyShannon(s):
+ n=0.0
+ dN={} # d[nucleotide]=occurence
+ for e in s:
+ n+=1
+ if e not in dN.keys():
+ dN[e]=0
+ dN[e]+=1
+
+ entShan=0
+ for base in dN.keys():
+ pN=dN[base]/n
+ entShan+=pN*(ln(pN)/LN2)
+ return entShan*-1
+
+def windowEntropy(s, wLen, wSlide):
+ # Compute Shannon entropy on a sequence s for each window of length wLen with a slide of length wSlide.
+ lE=[] # list of entropy
+ start=0 # start index
+ n=len(s)
+ for start in list(range(n))[::wSlide]:
+ end=start+wLen
+ if end >= n:
+ start=n-wLen
+ end=n-1
+ if start<0:
+ start=0
+ lE.append(entropyShannon(s[start:end]))
+ return lE
+##########################################################
+
+############### NEW FUNCTIONS -> REDUNDANCE ###############
+def idioticConsensus(s1,s2,maxEdit=3):
+ # Make consensus base per base (s1 and s2 have the same length)
+ cons=""
+ nN=0
+ #print(s1)
+ #print(s2)
+ for i in range(len(s1)):
+ if s1[i]==s2[i]:
+ cons+=s1[i]
+ else:
+ cons+="N"
+ if "N" not in [s1[i],s2[i]]:
+ nN+=1
+ i+=1
+ if not i%100 and nN>maxEdit:
+ #print(1)
+ #print(cons)
+ return [None,None]
+ if nN>maxEdit:
+ #print(2)
+ #print(cons)
+ return [None,None]
+ #print("OK")
+ #print(cons)
+ return [nN,cons]
+
+def consensus(s1,s2,mTB, trimN=True):
+ # 2 sequences and a traceback matrix
+ # Return one of the possible consensus, prioritazing match/mmatch, then T then L
+ # Create consensus sequence between 2 seq, adding "n" for indel and "N" for mismatches
+ # Will strip "N" of the consensus of trimN=True
+ nR=len(mTB)-1
+ nC=len(mTB[0])-1
+ cons=""
+ prev="D" # previous type of alignment
+ t=mTB[nR][nC]
+ while t!="E":
+ if prev in t: # If possible, continue the same type of alignment
+ do=prev
+ elif "D" in t: # else, priority to D
+ do="D"
+ elif "T" in t:
+ do="T"
+ else:
+ do="L"
+ if do=="D":
+ if s1[nC-1]==s2[nR-1]:
+ cons+=s1[nC-1]
+ elif s1[nC-1]=="I" or s2[nR-1]=="I":
+ cons+="I"
+ else:
+ cons+="N"
+ nR-=1
+ nC-=1
+ else:
+ cons+="I"
+ if do=="T":
+ nR-=1
+ else:
+ nC-=1
+ prev=do
+ t=mTB[nR][nC]
+ cons=cons[::-1]
+ if trimN:
+ cons=cons.strip("IN")
+ return cons
+
+
+def rc(s):
+ # reverse complement an ATGCN sequence
+ d={"A":"T","T":"A","G":"C","C":"G","N":"N","I":"I"}
+ return "".join([d[e] for e in s[::-1]])
+
+def levDist(a,b, match=MATCH, mm=MMATCH, gap=GAP, ext=1, semiLoc=False, minRC=True, maxEdit=3, doConsensus=True, trimN=True):
+ # Levenshtein distance between two strings a and b of length n and m. Option for Semi-Local method (smallest sequence must fully aligng anywhere on the largest sequence)
+ # Semi-Local: To do this, no initialisation of the first row (insertion are cost-free before aligning the first base of the smallest sequence) and cost-free insertion on the last row (insertion are cost-free after aligning the last base of the smallest sequence)
+ # a is the largest sequence (switched if needed in the function)
+ # ext : gap extension cost
+ # minRC : also do reverse complement computation and take the minimum distance of the two
+ # maxEdit : stop process if the final score will be greater or equal to this number. -1 to keep everything.
+ # doConsensus : compute consensus sequence
+ # Maybe add a maximum indel length allowed, but this could be complicated...
+ # 'N' is considered as a wildcard
+
+ cons=None
+ extD=ext
+ extI=ext
+ n=len(a)
+ m=len(b)
+ if n<m:
+ a,b=b,a
+ n,m=m,n
+
+ # 1) Create an (n+1)x(m+1) matrix
+ mLev=[[None for x in range(n+1)] for y in range(m+1)]
+ mTB=[["O" for x in range(n+1)] for y in range(m+1)]# traceback to know if gap is extended or not. O by default, T for top, L for left (can be TL/LT)
+
+ # 2) Intiliaze first column
+ mLev[0][0]=0
+ mTB[0][0]="E"
+ for r in range(1,m+1):
+ s=gap+(r-1)*ext
+ if maxEdit==-1 or s<maxEdit:
+ mLev[r][0]=s
+ mTB[r][0]="T"
+ # ... and the first row
+ for c in range(1,n+1):
+ mTB[0][c]="L"
+ if not semiLoc:
+ s=gap+(c-1)*ext
+ if maxEdit==-1 or s<maxEdit:
+ mLev[0][c]=s
+ else:
+ mLev[0][c]=0
+
+ # 3) Fill the matrix
+ ins=gap
+ dele=gap
+ startC=1 # Starting column to compute score = last None surrounded by Nones + 1, for the first line of None
+ r=1
+ while r <= m:
+ #for r in range(1,m+1):
+ if semiLoc and r==m:
+ ins=0
+ extI=0
+ isFirstNone=True # indicate if this is the first line of None
+ c=startC
+ allNone=True
+ while c <= n:
+ T=mLev[r-1][c] # score if coming from T
+ L=mLev[r][c-1] # score if coming from L
+ D=mLev[r-1][c-1] # score if coming from D
+ if [T,L,D]==[None,None,None]: # Then we can add None and go to next cell or line
+ if isFirstNone: # First line of None : we will start the next line on the last column surrounded by None
+ startC=c
+ else:
+ allNone=False
+ if isFirstNone:
+ isFirstNone=False
+ # N is a wildcard
+ if b[r-1] in WILDECARDS or a[c-1] in WILDECARDS or b[r-1] == a[c-1]:
+ sub=0
+ else:
+ sub=1
+
+ if T!=None:
+ if "T" in mTB[r-1][c]:
+ T+=extD
+ else:
+ T+=dele
+
+ if L!=None:
+ if "L" in mTB[r][c-1]:
+ L+=extI
+ else:
+ L+=ins
+ if D!=None:
+ D+=sub
+ minLev=min([x for x in [T,L,D] if x!=None])
+ if maxEdit!=-1 and minLev>=maxEdit:
+ minLev=None
+ add=""
+ if minLev==T:
+ add+="T"
+ if minLev==L:
+ add+="L"
+ if minLev==D:
+ add+="D"
+ if add!="":
+ mTB[r][c]=add
+ mLev[r][c]=minLev
+ c+=1
+ r+=1
+ if allNone: # If the whole line was made of None values
+ # Then the alignment failed
+ r=m+1 # Exit the loop
+
+ lev=mLev[m][n]
+ if doConsensus and lev!=None:
+ cons=consensus(a,b,mTB,trimN=trimN)
+ #print(mLev)
+ #print(mTB)
+ isRC=False
+ if minRC and lev==None:
+ lLevRC=levDist(a,rc(b),match=match, mm=mm, gap=gap, ext=ext, maxEdit=maxEdit, minRC=False,semiLoc=semiLoc,trimN=trimN)
+ levRC=lLevRC[0]
+ if levRC!=None and (lev==None or levRC<lev):
+ lev=levRC
+ isRC=True
+ cons=lLevRC[2]
+ return [lev,isRC,cons]
+
+def multLev(d, lID, triangular=True, semiLoc=False, lExclude=[], maxID=-1, k=41):
+ # Launch all successive Levenshtein distances between sequence in a dictionnary d[ID]=[[string1,string2],[len1,len2]] if length diff between sequence < threshold
+ # For seq1 seq2 seq3 seq4, true seq1-seq2 compression:
+ # - If compressed: try seq3-seq4 compression
+ # - If not compressed: try seq2-seq3 compression
+ # triangular is not useful
+ # smiLoc=True for semi-local alignment
+ # lExclude is a list of cycle ID tuple indicating cycles allready compared but that could not be compressed. Used to avoid recomputing their edit distance
+ # maxID: maximum number of cycle in a BCC to try compression. -1 for all.
+ # Return a dictionnary d[cycle1][cycle2]=[levDistLow,levDistUp,consensusLow,consensusUpSeq,consensusUpVar] (and the same for d[cycle1][cycle2] if triangular=True)
+ lComp=[]
+ nID=len(lID)
+ if len(lID)<2 or (maxID!=-1 and len(lID)>maxID):
+ return [{},lComp]
+ dLev={}
+ j=0
+ while j+1 < nID:
+ ID1=lID[j]
+ l1l=d[ID1][1][1]
+ l1u=d[ID1][1][0]
+ ID2=lID[(j+1)]
+ l2l=d[ID2][1][1]
+ l2u=d[ID2][1][0]
+ levL=None
+ levU=None
+ addErrorMin=int(l2u/300)
+ addErrorMax=int(l1u/100)
+ #print("Comparing "+ID1+" and "+ID2)
+ if (ID1,ID2) not in lExclude and abs(l1l-l2l)<=BASEDIFFPATH_LOW and abs(l1u-l2u)<=BASEDIFFPATH_UP+addErrorMin: # Length difference of lower path < 6 and length diff of upper path < 3
+ # LOW COMPARISON
+ # We had the variable sequence to the middle of the lower path
+ var1=d[ID1][0][2]
+ var2=d[ID2][0][2]
+ seqLow1=d[ID1][0][1]
+ #seqLow1=seqLow1[:k]+var1+seqLow1[k:]
+ seqLow2=d[ID2][0][1]
+ #seqLow2=seqLow2[:k]+var2+seqLow2[k:]
+ levL,isRC,consL=levDist(seqLow1,seqLow2,maxEdit=BASEMM_LOW,ext=EXT,semiLoc=True) # We allow an high error rate on the lower path
+ if levL!=None:
+ # UP COMPARISON
+ seqUp1=d[ID1][0][0]
+ seqUp2=d[ID2][0][0]
+ if isRC: # if reverse complement was used for lower path, use it for upper path
+ seqUp2=rc(seqUp2)
+ var2=rc(var2)
+ # Variable path comp
+ lLevV=levDist(var1,var2,mm=MMATCH/(addErrorMax+1),ext=EXT,semiLoc=False,minRC=False,trimN=False)
+ levV=lLevV[0]
+ consVar=lLevV[2]
+ if levV!=None: # Variable path align correctly
+ ## Test if we are in a big IR cluster
+ idioticOK=False
+ if len(seqUp1)==len(seqUp2) and l1u>=MAXLENGTHIDIOTIC: # Both path of the same length and upper path > 2000nc, probably a big IR
+ levU,consU=idioticConsensus(seqUp1,seqUp2,maxEdit=BASEMM_UP+addErrorMax-levV)
+ if levU!=None:
+ idioticOK=True
+ ##
+ if not idioticOK and l1u<MAXLENGTHALIGN:
+ # Do alignment
+ lLevU=levDist(seqUp1,seqUp2,maxEdit=BASEMM_UP-levV,mm=MMATCH/(addErrorMax+1),ext=EXT,semiLoc=False,minRC=False,trimN=False)
+ levU=lLevU[0]
+ consU=lLevU[2]
+ # We know if we compress or not if levU!=None
+ if levU==None: # We will remmember to not compare these two paths because their lower or upper path are too divergent
+ lExclude.append((ID1,ID2))
+ else:
+ #consLseq=consL[:k]+consL[-k:]
+ #consVar=consL[k:-k]
+ j+=1
+ if ID1 not in dLev.keys():
+ dLev[ID1]={}
+ dLev[ID1][ID2]=[levL,levU,consL,consU,consVar]
+ lComp.append((ID1,ID2))
+ if not triangular:
+ if not ID2 in dLev.keys():
+ dLev[ID2]={}
+ dLev[ID2][ID1]=[levL,levU,consL,consU,consVar]
+ j+=1
+ return [dLev,lComp]
+
+def readFasta4(f, k=41, rmEntropy=True, entropy_threshold=ENTROPYMAX, dBCC2lc={}):
+ # Read 4 lines of a fasta file from KisSplice (1 cycle)
+ # return [ [bcc, cycle, type, length_up, length_low], [seq_up, seq_low, seq_var] ]
+ # return KO if the entropy filter failed (WARNING: do not try to recursively call readFasta4 in this case as the max recursive instance can easily be reached)
+ # seq_var is the sequence from the variable part of the upper path that can be find either at its begining or ending
+ # seq_up is the sequence from the variable part without seq_var
+ head=f.readline().strip()
+ if head=="":
+ return ""
+ lHead=head.split("|")
+ bcc=lHead[0].split("_")[1]
+ c=lHead[1].split("_")[1]
+ t=lHead[2].split("_")[1]
+ lup=int(lHead[3].split("_")[-1])
+ mk=min(lup,2*k)
+ seq=f.readline().strip()
+ seqUp="".join([x for x in seq if x.isupper()])
+ head=f.readline().strip()
+ lHead=head.split("|")
+ llow=int(lHead[3].split("_")[-1])
+ seq=f.readline().strip()
+ seqLow="".join([x for x in seq if x.isupper()])
+ lup=lup-llow
+ if rmEntropy:
+ #ent=entropyShannon(seqUp)
+ lEnt=windowEntropy(seqUp, WLEN, WSLIDE)
+ ent=max(lEnt)
+ if ent<entropy_threshold:
+ #print("\n".join([">bcc_"+bcc+"|Cycle_"+c,seqUp,">lower",seqLow]))
+ if not bcc in dBCC2lc.keys():
+ dBCC2lc[bcc]=[]
+ dBCC2lc[bcc].append([c,str(lEnt)])
+ return "KO"
+ # SeqUp will be the upper sequence without the potential repeated bases at the begining/end of the upperpath, ie :
+ # >bcc_9988|Cycle_0|Type_1|upper_path_length_163
+ # GGCTGCAACCGAGTCTTCATAGAAGAGAATCTGCTGTACCTCGGAATCCTCGCTGAAGTCTTCGGTGACGGTAGAGGAGGAGGCCTGCCGGGGGAGCTTGGCCTCGTATGCCATGACGCTCCACCTGTCCAGCATCTTGGTGCTGGCTCTCTCCAACTTCTCC
+ # >bcc_9988|Cycle_0|Type_1|lower_path_length_78
+ # GGCTGCAACCGAGTCTTCATAGAAGAGAATCTGCTGTACCTGTCCAGCATCTTGGTGCTGGCTCTCTCCAACTTCTCC
+ # In this exemple, the upper sequence can either be :
+ # CGGAATCCTCGCTGAAGTCTTCGGTGACGGTAGAGGAGGAGGCCTGCCGGGGGAGCTTGGCCTCGTATGCCATGACGCTCCACCT
+ # OR
+ # ACCTCGGAATCCTCGCTGAAGTCTTCGGTGACGGTAGAGGAGGAGGCCTGCCGGGGGAGCTTGGCCTCGTATGCCATGACGCTCC
+ # Because of the starting/ending ACCT
+ # We will report seqUp=CGGAATCCTCGCTGAAGTCTTCGGTGACGGTAGAGGAGGAGGCCTGCCGGGGGAGCTTGGCCTCGTATGCCATGACGCTCC and var=ACCT
+ # The size of var is 2*k-lowerPathLength
+ # In can happen that the upper path is < 2*k, in this case we have to use the lvar=lup-llow
+ lvar=mk-llow
+ var=seqUp[k-lvar:k]
+ seqUp=seqUp[k:k+lup-lvar]
+ # It is possible that seqUp is empty, if the path of the var is the same as the upper path, ie
+ # >bcc_9962|Cycle_0|Type_1|upper_path_length_82
+ # ATAAAGGATATGTTGAATACACCTTTGTGTCCTTCACACAGCAGTTTACATCCAGTGCTGTTACCTTCAGATGTATTTGACC
+ # >bcc_9962|Cycle_0|Type_1|lower_path_length_79
+ # ATAAAGGATATGTTGAATACACCTTTGTGTCCTTCACACAGTTTACATCCAGTGCTGTTACCTTCAGATGTATTTGACC
+ # In this exemple, seqUp='' and var="CAG"
+ # So we simply invert them
+ if seqUp=="":
+ seqUp,var=var,seqUp
+ return [ [bcc, c, t, lup, llow], [seqUp, seqLow, var]]
+
+def compress(dLev, dSeq, lCycleOrder, lExclude):
+ # Remove one of the paths from dSeq and lCycleOrder
+ # Return dictionnary of compressed paths
+ #print(dLev)
+ #print("BEFORE COMPRESS")
+ #print(dSeq)
+ #print(lCycleOrder)
+ for ID1 in dLev.keys():
+ for ID2 in dLev[ID1].keys():
+ dSeq[ID1][0][0]=dLev[ID1][ID2][3]
+ dSeq[ID1][0][2]=dLev[ID1][ID2][4]
+ dSeq[ID1][0][1]=dLev[ID1][ID2][2]
+ dSeq[ID1][1][0]=len(dSeq[ID1][0][0])+len(dSeq[ID1][0][2])
+ dSeq[ID1][1][1]=len(dSeq[ID1][0][1])
+ del dSeq[ID2]
+ del lCycleOrder[lCycleOrder.index(ID2)]
+ # Delete pairs containing either ID1 or ID2 from lExclude
+ lExclude=[lExclude[i] for i in range(len(lExclude)) if ID1 not in lExclude[i] and ID2 not in lExclude[i]]
+ del dLev
+ #print("AFTER COMPRESS")
+ #print(dSeq)
+ #print(lCycleOrder)
+
+def addComp(d,l):
+ # Add compressed paths to the dictionnary of compressed path
+ # d : d[cycle]=[compCycle1, compCycle2, ...]
+ # l : [(cycle1, cycleComp1), (cycle2, cycleComp2), ...] cycleCompX is compressed in cycleX
+ # cycleCompX can be a key of d
+ for cycle,comp in l:
+ if cycle in d.keys():
+ d[cycle].append(comp)
+ else:
+ d[cycle]=[comp]
+ if comp in d.keys():
+ d[cycle].extend(d[comp])
+ del d[comp]
+
+def compressBcc(dBcc, cBcc, dSeq, lCycleOrder, dBcc2cycleComp, dBCC2size, dBccLen2nCompress, k):
+ # t is the type of event
+ # Compressed sequences will be written in fComp, an open writable file
+ # Do the whole compression of a BCC
+ lExclude=[] # list of cycle pairs that can not be compressed
+ # dBccLen2nCompress is not mendatory, contain useful(?) informations. d[BCCsize]=[nBCC, number of compressed path]
+ dBcc2cycleComp[cBcc]={} # not mendatory, contain useful(?) informations. d[bcc][cycle]=[compressed cycles]
+ BCCsize=len(dSeq.keys())
+ dBCC2size[cBcc]=BCCsize # not mendatory, contain useful(?) informations. d[bcc]=size
+ if not BCCsize in dBccLen2nCompress.keys():
+ dBccLen2nCompress[BCCsize]=[0,0]
+ dBccLen2nCompress[BCCsize][0]+=1
+ #print("BCC "+cBcc+" (of "+str(len(dSeq.keys()))+" cycles)")
+ dBcc[cBcc],lComp=multLev(dSeq,lCycleOrder,lExclude=lExclude,k=k)
+ while dBcc[cBcc] != {}: # While we have some compression to do
+ #print(lComp)
+ dBccLen2nCompress[BCCsize][1]+=len(lComp)
+ addComp(dBcc2cycleComp[cBcc],lComp)
+ compress(dBcc[cBcc], dSeq, lCycleOrder, lExclude)
+ dBcc[cBcc],lComp=multLev(dSeq,lCycleOrder,lExclude=lExclude,k=k)
+ #print(dBcc[cBcc])
+ #print(dBccLen2nCompress)
+ #print(dBcc2cycleComp)
+
+def writeCompressedCycles(dSeq, cBcc, t, fComp, kval):
+ # Write info in dSeq to fComp
+ # dSeq: d[cycle]=[ [seqUp, seqLow, var], [lenUp, lenLow] ]
+ # head format: >bcc_[cBcc]|Cycle_[cycle]|Type_[t]|upper/lower_path_length_[length]
+ headBcc=">bcc_"+cBcc
+ headType="Type_"+t
+ for cycle in dSeq.keys():
+ lInfo=dSeq[cycle]
+ seqLow=lInfo[0][1]
+ seqUp=seqLow[:kval]+lInfo[0][0]+lInfo[0][2]+seqLow[kval:]
+ lenUp=str(lInfo[1][0]+lInfo[1][1])
+ lenLow=str(lInfo[1][1])
+ headCycle="Cycle_"+cycle
+ headLenUp="upper_path_length_"+lenUp
+ headLenLow="lower_path_length_"+lenLow
+ head="|".join([headBcc,headCycle,headType])
+ headUp="|".join([head,headLenUp])
+ headLow="|".join([head,headLenLow])
+ fComp.write("\n".join([headUp,seqUp,headLow,seqLow])+"\n")
+
+def splitT1T234(fName, fNameT1, fNameT234):
+ f=open(fName,"r")
+ f1=open(fNameT1,"w")
+ f234=open(fNameT234,"w")
+ retype = re.compile('Type_\d+')
+ line=f.readline()
+ while line:
+ t=retype.search(line).group()
+ if t=="Type_1":
+ oF=f1
+ else:
+ oF=f234
+ oF.write(line)
+ line=f.readline()
+ oF.write(line)
+ line=f.readline()
+ oF.write(line)
+ line=f.readline()
+ oF.write(line)
+ line=f.readline()
+ f.close()
+ f1.close()
+ f234.close()
+
+def redundancyAndLowComplexityRemoval(workdir, mainFileName, keep_rd=False, keep_lc=False, lc_ent=ENTROPYMAX, get_rd_info=True, get_lc_info=True, t1o=False, kval=41):
+ # Main function for redundancy and low-complexity bubbles removal
+ # workdir: str, working directory
+ # mainFileName: str, fasta file name containing all types of bubbles
+ # keep_rd: boolean, do we remove redundancy?
+ # keep_lc: boolean, do we keep low-complexity bubbles?
+ # lc_ent: int, Shannon Entropy threshold to define a bubble as low-complexity (if below this value)
+ # get_rd_info: boolean, do we print useful(?) informations about redundancy removal in some files?
+ # get_lc_info: boolean, do we print useful(?) informations about low-complexity removal in some files?
+ # kval: int, k-mers value
+ # return list of files to copy from the workdir to the resultdir, will replace mainFile by a new file if needed, and will create new files in the workdir
+
+ # Do we need to do anything?
+ if keep_rd and keep_lc and not t1o:
+ return []
+
+ print("\n" + getTimestamp() + "--> Removing low-complexity/redundant bubbles...")
+ printlg("\n" + getTimestamp() + "--> Removing low-complexity/redundant bubbles...")
+
+ toMove=[] # list of files to move to the result directory (will be returned)
+ toRm=[] # list of files to remove
+
+ # 1) Divide Type_1 bubbles in one file, other bubbles in another file
+ # The Type_1 file will be moved to the result directory
+ t1fileName="all_bcc_type1.fa"
+ t234fileName="all_bcc_type234.fa"
+ if not keep_lc or not keep_rd:
+ toMove.append(t1fileName)
+ toRm.append(t234fileName)
+ splitT1T234("/".join([workdir,mainFileName]), "/".join([workdir,t1fileName]), "/".join([workdir,t234fileName]))
+
+ if not keep_rd or not keep_lc:
+ # 2) Define some dictionnaries...
+ dSeq={} # d[cycle]=[ [seqUp, seqLow, var], [lenUp, lenLow] ]
+ dBcc={} # d[bcc]=dLev with dLev = d[cycle1][cycle2]=[levDistLow,levDistUp,consensusLow,consensusUpSeq,consensusUpVar]
+ dBcc2cycleComp={} # d[bcc][cycle]=[compressed cycles]
+ dBccLen2nCompress={} # d[BCCsize]=[nBCC, number of compressed path]
+ dBCC2size={} # d[bcc]=size
+ dBCC2lc={} # d[bcc]=[ [removed cycle due to low complexity, entropy value], ...]
+ # ... and an output file
+ t1fileNameComp="all_bcc_type1_compressed.fa"
+ fComp=open("/".join([workdir,t1fileNameComp]), "w")
+ if not keep_rd:
+ toMove.append(t1fileNameComp)
+
+ # 3) Open and read first line of Type_1 file
+ f=open("/".join([workdir,t1fileName]),"r")
+ lFasta="KO"
+ while lFasta and lFasta=="KO":
+ lFasta=readFasta4(f, k=kval, rmEntropy=not keep_lc, entropy_threshold=lc_ent, dBCC2lc=dBCC2lc) # [ [bcc, cycle, type, length_up, length_low], [seq_up, seq_low, seq_var] ]
+ # Fasta file not empty and the cycle was not removed by entropy filter (!=KO)
+ if lFasta:
+ # First interesting line of fasta
+ cBcc=lFasta[0][0] # current BCC
+ dSeq[lFasta[0][1]]=[lFasta[1], lFasta[0][3:5]] # we add the sequences informations associated to this cycle to dSeq
+ lCycleOrder=[lFasta[0][1]] # list of cycles ID order as in the fasta file (close cycles have less divergence)
+ # 4) Read the whole fasta file and compress BCC
+ while lFasta:
+ # Read a new cycle
+ lFasta=readFasta4(f, k=kval, rmEntropy=not keep_lc, entropy_threshold=lc_ent, dBCC2lc=dBCC2lc) # [ [bcc, cycle, type, length_up, length_low], [seq_up, seq_low, seq_var] ]
+ if lFasta and lFasta!="KO": # EOF or removed cycle due to low complexity
+ if cBcc!=lFasta[0][0]: # New bcc
+ # Compress the cycles from the previous BCC, if needed
+ if not keep_rd:
+ compressBcc(dBcc, cBcc, dSeq, lCycleOrder, dBcc2cycleComp, dBCC2size, dBccLen2nCompress, kval)
+ writeCompressedCycles(dSeq, cBcc, "1", fComp, kval)
+ cBcc=lFasta[0][0] # New current BCC
+ dSeq={}
+ lCycleOrder=[]
+ dSeq[lFasta[0][1]]=[lFasta[1], lFasta[0][3:5]] # we add the sequences informations associated to this cycle to dSeq
+ lCycleOrder.extend([lFasta[0][1]]) # list of cycles ID order as in the fasta file (close cycles have less divergence)
+ # Compress the cycles of the last BCC
+ if not keep_rd:
+ compressBcc(dBcc, cBcc, dSeq, lCycleOrder, dBcc2cycleComp, dBCC2size, dBccLen2nCompress, kval)
+ writeCompressedCycles(dSeq, cBcc, "1", fComp, kval)
+
+ f.close()
+ fComp.close()
+
+ # 5) Make informations files
+ if get_rd_info:
+ fNameSummary="get_redundancy_info_summary.tsv"
+ fNameRd="get_redundancy_info_compressed_bubbles.tsv"
+ toMove.extend([fNameSummary,fNameRd])
+ makeSummaryRd("/".join([workdir,fNameSummary]), "/".join([workdir,fNameRd]), dBcc2cycleComp, dBccLen2nCompress)
+
+ if get_lc_info:
+ fNameLc="get_low-complexity_info.tsv"
+ toMove.append(fNameLc)
+ makeSummaryLc("/".join([workdir,fNameLc]), dBCC2lc)
+
+ # 6) Write a new mainFile, combining filtered type 1 and type 234 or type 1 only
+ if t1o:
+ if not keep_rd or not keep_lc:
+ os.system("cat "+"/".join([workdir,t1fileNameComp])+" > "+"/".join([workdir,mainFileName]))
+ else:
+ os.system("cat "+"/".join([workdir,t1fileName])+" > "+"/".join([workdir,mainFileName]))
+ else:
+ os.system("cat "+"/".join([workdir,t1fileNameComp])+" "+"/".join([workdir,t234fileName])+" > "+"/".join([workdir,mainFileName]))
+
+ # 7) Remove some files
+ for fRm in toRm:
+ os.system("rm "+"/".join([workdir,fRm]))
+
+ print(getTimestamp() + "--> Done!")
+ printlg(getTimestamp() + "--> Done!")
+
+ return toMove
+
+def makeSummaryLc(fNameLc, dBCC2lc):
+ # dBCC2lc : d[bcc]=[ [removed cycle due to low complexity, entropy value], ...]
+ fLc=open(fNameLc, "w")
+
+ # low-complexity file
+ # bcc removed_cycle entropy_value
+ head="\t".join(["bcc", "removed_cycle", "shannon_entropy"])
+ fLc.write(head)
+ for bcc in dBCC2lc.keys():
+ for lInfo in dBCC2lc[bcc]:
+ rmCycle=lInfo[0]
+ ent=str(lInfo[1])
+ fLc.write("\n"+"\t".join([bcc, rmCycle, ent]))
+ fLc.close()
+
+
+
+
+def makeSummaryRd(fNameSummary, fNameRd, dBcc2cycleComp, dBccLen2nCompress):
+ # dBcc2cycleComp : d[bcc][cycle]=[compressed cycles]
+ # dBccLen2nCompress : d[BCCsize]=[nBCC, number of compressed path]
+ fSum=open(fNameSummary, "w")
+ fRd=open(fNameRd, "w")
+
+ # redundancy file
+ # bcc consensus_cycle compressed_cycles nCompressed
+ headRd="\t".join(["bcc","consensus_cycle","compressed_cycles","nCompressed"])
+ fRd.write(headRd)
+ for bcc in dBcc2cycleComp.keys():
+ for cycle in dBcc2cycleComp[bcc].keys():
+ lComp=dBcc2cycleComp[bcc][cycle]
+ if lComp!=[]:
+ fRd.write("\n"+"\t".join([bcc, cycle, ",".join(lComp), str(len(lComp))]))
+ fRd.close()
+
+ # summary file
+ # bcc_size nBcc nCycles nCompressedCycles %compressed nRemainingCycles
+ headSum="\t".join(["bcc_size","nBcc","nCycles","nCompressedCycles", "%compressed", "nRemainingCycles"])
+ fSum.write(headSum)
+ for bccSize in sorted(list(dBccLen2nCompress.keys())):
+ lInfo=dBccLen2nCompress[bccSize]
+ nBcc=lInfo[0]
+ nComp=lInfo[1]
+ nCycles=bccSize*nBcc
+ nRemain=nCycles-nComp
+ pComp=round((nComp/nCycles)*100)
+ fSum.write("\n"+"\t".join([str(bccSize), str(nBcc), str(nCycles), str(nComp), str(pComp)+"%", str(nRemain)]))
+ fSum.close()
+
+
+###########################################################
+
# print str to the logFile
def printlg (*args):
global logFile
@@ -470,15 +1129,13 @@ def concatenate_graph_all_log_bcc_to_all_bcc_type0(workdir, kval, output_snps):
def check_read_coverage_and_sort_all_bubbles(internal_bindir, readfiles, workdir, outdir, kval, output_snps, infix_name,
- countsMethods, minOverlap, substitutions, getMappingInfo, stranded, strandedAbsoluteThreshold, strandedRelativeThreshold, nbprocs, verbose = False):
- print("\n" + getTimestamp() + "--> Computing read coherence and coverage...")
- printlg("\n" + getTimestamp() + "--> Computing read coherence and coverage...")
+ countsMethods, minOverlap, substitutions, substitutionsSNP, getMappingInfo, stranded, strandedAbsoluteThreshold, strandedRelativeThreshold, nbprocs, verbose = False):
# Two KisSreads executions, one for type 0 one for type 1234
# Du to the k extension, anchor should be of size k+1 for SNP
commandLineType0=""
if output_snps > 0:
- commandLineType0 = internal_bindir+"/ks_kissreadsSNPS "+workdir+"/all_bcc_type0_"+str(kval)+" "+readfiles+" -i 5 -S 25 -O "+str(kval+minOverlap)+" -o "+ workdir+"/coherentType0.fa -u "+workdir+"/uncoherentType0.fa -d " + str(substitutions) + " -c 1 -n -t "+str(nbprocs)
+ commandLineType0 = internal_bindir+"/ks_kissreadsSNPS "+workdir+"/all_bcc_type0_"+str(kval)+" "+readfiles+" -i 5 -S 25 -O "+str(kval+minOverlap)+" -o "+ workdir+"/coherentType0.fa -u "+workdir+"/uncoherentType0.fa -d " + str(substitutionsSNP) + " -c 1 -n -t "+str(nbprocs)
if stranded:
commandLineType0+=" -x -a " + str(strandedAbsoluteThreshold) + " -r " + str(strandedRelativeThreshold) + " "
if getMappingInfo:
@@ -493,6 +1150,9 @@ def check_read_coverage_and_sort_all_bubbles(internal_bindir, readfiles, workdir
if os.path.exists(workdir+"/uncoherentType0.fa.explanations"):
shutil.move(workdir+"/uncoherentType0.fa.explanations", outdir+"/uncoherentType0.fa.explanations")
+ print("\n" + getTimestamp() + "--> Computing read coherence and coverage...")
+ printlg("\n" + getTimestamp() + "--> Computing read coherence and coverage...")
+
# no n options anymore
commandLineType1234 = internal_bindir+"/ks_kissreadsSplice "+workdir+"/all_bcc_type1234_"+str(kval)+" "+readfiles+" -i 5 -k "+str(kval)+" -S 25 -O "+str(kval+minOverlap)+" -o "+workdir+"/coherentType1234.fa -u "+workdir+"/uncoherentType1234.fa -d " + str(substitutions) + " -c 1 -j " + countsMethods +" -l " + str(minOverlap) +" -t "+str(nbprocs)
if stranded:
@@ -720,6 +1380,8 @@ def main():
help="maximal number of bubbles enumeration in each bcc. If exceeded, no bubble is output for the bcc (default 100M)")
parser.add_argument('--mismatches', action = "store", dest = "mism", default = 2, type = int,
help="Maximal number of substitutions authorized between a read and a fragment (for quantification only), default 2. If you increase the mismatch and use --counts think of increasing min_overlap too.")
+ parser.add_argument('--mismatchesSNP', action = "store", dest = "mismSNP", default = 0, type = int,
+ help="Maximal number of substitutions authorized between a read and a fragment (for quantification only) for SNP, default 0.")
parser.add_argument('--counts', action = "store", dest = "countsMethod", default = "2", help="0,1 or 2 . Changes how the counts will be reported. If 0 : total counts, if 1: counts on junctions, if 2 (default): all counts. see User guide for more information ")
parser.add_argument('--min_overlap', action = "store", dest = "minOverlap", default = 5, type=int, help="Set how many nt must overlap a junction to be counted by --counts option. Default=5. see User guide for more information ")
parser.add_argument('--timeout', action='store', dest="timeout", default=TIMEOUT,
@@ -737,7 +1399,12 @@ def main():
parser.add_argument('--stranded', action="store_true", dest="stranded", default = False, help="Execute kissreads in stranded mode.")
parser.add_argument('--strandedAbsoluteThreshold', action = "store", dest = "strandedAbsoluteThreshold", default = 3, type=int, help="Sets the minimum number of reads mapping to a path of a bubble in a read set is needed to call a strand.")
parser.add_argument('--strandedRelativeThreshold', action = "store", dest = "strandedRelativeThreshold", default = 0.75, help="If a strand is called for a path of a bubble in a read set, but the proportion of reads calling this strand is less than this threshold, then the strand of the path is set to '?' (any strand - not enough evidence to call a strand).")
-
+ parser.add_argument('--keep-redundancy', action="store_true", dest="keep_rd", default = False, help="Keep the Type_1 redundant cycles in the result file.")
+ parser.add_argument('--keep-low-complexity', action="store_true", dest="keep_lc", default = False, help="Keep the low-complexity Type_1 cycles in the result file.")
+ parser.add_argument('--lc-entropy-threshold', action = "store", dest = "lc_ent", default = ENTROPYMAX, type=int, help="Cycles with a Shannon entropy value for their upper path below this value will be removed (use --keep-low-complexity to keep them).")
+ parser.add_argument('--get-redundance-info', action="store_true", dest="get_rd_info", default = False, help="Creates files with informations on compressed redundant cycles.")
+ parser.add_argument('--get-low-complexity-info', action="store_true", dest="get_lc_info", default = False, help="Creates a file with informations on removed low-complexity cycles.")
+ parser.add_argument('--type1-only', action="store_true", dest="t1o", default = False, help="Only quantify Type 1 bubbles (alternative splicing events, MAJOR SPEED UP with -b > 10 BUT all other bubbles will not appear in the result file).")
# ------------------------------------------------------------------------
# Parse and interpret command line arguments
# ------------------------------------------------------------------------
@@ -754,7 +1421,7 @@ def main():
# Create the log file
# ------------------------------------------------------------------------
global logFile, logFileName
- logFileName = outdir+"/kissplice_log_summary_"+time.strftime("%H-%M-%S")+"_"+time.strftime("%d-%m-%Y")+"_"+str(randint(0, 1000000))
+ logFileName = outdir+"/kissplice_log_summary_"+time.strftime("%Y-%m-%d")+"_"+time.strftime("%H-%M-%S")+"_"+str(randint(0, 1000000))
logFile = open(logFileName, 'w')
@@ -870,8 +1537,8 @@ def main():
if not nobuild:
t = time.time()
build_graph(internal_bindir, workdir, readfiles, kval, graphfile, min_cov, options.nbprocs, options.verbose)
- print("Elapsed time: ", (time.time() - t), " seconds")
- printlg("Elapsed time: ", (time.time() - t), " seconds")
+ print("Elapsed time: ", round(time.time() - t,1), " seconds")
+ printlg("Elapsed time: ", round(time.time() - t,1), " seconds")
# ------------------------------------------------------------------------
# Error removal
@@ -882,8 +1549,8 @@ def main():
else:
exec_error_removal = False
- print("Elapsed time: ", (time.time() - t), " seconds")
- printlg("Elapsed time: ", (time.time() - t), " seconds")
+ print("Elapsed time: ", round(time.time() - t,1), " seconds")
+ printlg("Elapsed time: ", round(time.time() - t,1), " seconds")
# ------------------------------------------------------------------------
@@ -891,8 +1558,8 @@ def main():
# ------------------------------------------------------------------------
t = time.time()
run_modules(internal_bindir, workdir, graphfile, kval, options.min_relative_cov, options.verbose, options.output_context, exec_error_removal)
- print("Elapsed time: ", (time.time() - t), " seconds")
- printlg("Elapsed time: ", (time.time() - t), " seconds")
+ print("Elapsed time: ", round(time.time() - t,1), " seconds")
+ printlg("Elapsed time: ", round(time.time() - t,1), " seconds")
# ------------------------------------------------------------------------
@@ -902,20 +1569,30 @@ def main():
enumerate_all_bubbles(internal_bindir, workdir, outdir, kval, options.bval, output_snps, min_edit_dist, max_cycles, \
UL_MAX, LL_MAX, LL_MIN, float(options.timeout), options.nbprocs, options.verbose, \
options.output_context, options.output_path, options.output_branch_count, options.experimental, options.max_memory)
- print("Elapsed time: ", (time.time() - t), " seconds")
- printlg("Elapsed time: ", (time.time() - t), " seconds")
+ print("Elapsed time: ", round(time.time() - t,1), " seconds")
+ printlg("Elapsed time: ", round(time.time() - t,1), " seconds")
# ------------------------------------------------------------------------
- # Check read coverage (optionnal) and sort bubbles
+ # Sort and remove redundancy/low-complexity bubbles (optionnal), only keep type_1 events (optionnal)
# ------------------------------------------------------------------------
t = time.time()
nb = sort_all_bubbles(internal_bindir, readfiles, workdir, outdir, kval, output_snps, infix_name, not only_graph, options.verbose)
+ filesToMove=redundancyAndLowComplexityRemoval(workdir, "all_bcc_type1234_"+str(kval), options.keep_rd, options.keep_lc, options.lc_ent, options.get_rd_info, options.get_lc_info, options.t1o, kval)
+ for fToMove in filesToMove:
+ shutil.move("/".join([workdir,fToMove]), "/".join([outdir,fToMove]))
+ print("Elapsed time: "+str(round(time.time() - t,1))+" seconds")
+ printlg("Elapsed time: "+str(round(time.time() - t,1))+" seconds")
+
+ # ------------------------------------------------------------------------
+ # Check read coverage (optionnal)
+ # ------------------------------------------------------------------------
+ t = time.time()
if not only_graph:
- nb = check_read_coverage_and_sort_all_bubbles(internal_bindir, readfiles, workdir, outdir, kval, output_snps, infix_name, countsMethod, minOverlap, options.mism, options.get_mapping_info, options.stranded, options.strandedAbsoluteThreshold, options.strandedRelativeThreshold, options.nbprocs, options.verbose)
+ nb = check_read_coverage_and_sort_all_bubbles(internal_bindir, readfiles, workdir, outdir, kval, output_snps, infix_name, countsMethod, minOverlap, options.mism, options.mismSNP, options.get_mapping_info, options.stranded, options.strandedAbsoluteThreshold, options.strandedRelativeThreshold, options.nbprocs, options.verbose)
- print("Elapsed time: ", (time.time() - t), " seconds\n")
- printlg("Elapsed time: ", (time.time() - t), " seconds\n")
+ print("Elapsed time: ", round(time.time() - t,1), " seconds\n")
+ printlg("Elapsed time: ", round(time.time() - t,1), " seconds\n")
if only_graph:
print("\n\n \t\t ******** We are done, final results are in files "+outdir+"/results_"+infix_name+"_type_*.fa **********")
=====================================
man/CMakeLists.txt
=====================================
@@ -1,5 +1,4 @@
# Fill manpage template with version and date
# TODO maybe use tools like help2man to automate manpage, considering this is mostly a --help message ?
-string(TIMESTAMP MANPAGE_DATE "%Y-%m-%d")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/kissplice.in.1" "${CMAKE_CURRENT_BINARY_DIR}/kissplice.1" @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/kissplice.1" DESTINATION "${CMAKE_INSTALL_MANDIR}/man1")
\ No newline at end of file
=====================================
man/kissplice.in.1
=====================================
@@ -1,4 +1,4 @@
-.TH KISSPLICE "1" "@MANPAGE_DATE@" "kissplice @PROJECT_VERSION@" "User Commands"
+.TH KISSPLICE "1" "@CONFIGURE_DATE@" "kissplice @PROJECT_VERSION@" "User Commands"
.SH NAME
kisSplice \- local assembly of SNPs, indels and AS events
.SH SYNOPSIS
@@ -129,3 +129,21 @@ Sets the minimum number of reads mapping to a path of a bubble in a read set is
.TP
\fB\-\-strandedRelativeThreshold\fR
If a strand is called for a path of a bubble in a read set, but the proportion of reads calling this strand is less than this threshold, then the strand of the path is set to '?' (any strand - not enough evidence to call a strand).
+.TP
+\fB\-\-keep-redundancy\fR
+Keep the Type_1 redundant cycles in the result file.
+.TP
+\fB\-\-keep-low-complexity\fR
+Keep the low-complexity Type_1 cycles in the result file.
+.TP
+\fB\-\-lc-entropy-threshold\fR
+Cycles with a Shannon entropy value for their upper path below this value will be removed (use --keep-low-complexity to keep them).
+.TP
+\fB\-\-get-redundance-info\fR
+Creates files with informations on compressed redundant cycles.
+.TP
+\fB\-\-get-low-complexity-info\fR
+Creates a file with informations on removed low-complexity cycles.
+.TP
+\fB\-\-type1-only\fR
+Only quantify Type 1 bubbles (alternative splicing events, MAJOR SPEED UP with -b > 10 BUT all other bubbles will not appear in the result file).
\ No newline at end of file
=====================================
tests/integration_tests/CMakeLists.txt
=====================================
@@ -9,7 +9,6 @@ add_test(KisspliceBasicTest "${BUILD_BINDIR}/kissplice" -h)
# Comparing the graph and the count file
# ============================================================================
add_test(KisspliceDBGTest "${CMAKE_CURRENT_SOURCE_DIR}/kisspliceDBGTest.py" "${BUILD_BINDIR}")
-set_tests_properties(KisspliceDBGTest PROPERTIES PASS_REGULAR_EXPRESSION "test SUCCESSFUL")
# ============================================================================
# Tests on: /data/HBM75brain_100000.fasta
=====================================
tests/integration_tests/data/graph_HBM75brain_100000_HBM75liver_100000_k25.abundance0
=====================================
@@ -12108,4 +12108,4 @@
97.4
97.8
97.8
-99.2
\ No newline at end of file
+99.2
=====================================
tests/integration_tests/data/graph_HBM75brain_100000_HBM75liver_100000_k25.edges0
=====================================
@@ -5339,4 +5339,4 @@ RR
RR
RR
RR
-RR
\ No newline at end of file
+RR
=====================================
tests/integration_tests/data/graph_HBM75brain_100000_HBM75liver_100000_k25.nodes0
=====================================
@@ -12108,4 +12108,4 @@ TTTTTTTTTTTTTTTTTTTTTTTGCAG
TTTTTTTTTTTTTTTTTTTTTTTGG
TTTTTTTTTTTTTTTTTTTTTTTTA
TTTTTTTTTTTTTTTTTTTTTTTTC
-TTTTTTTTTTTTTTTTTTTTTTTTG
\ No newline at end of file
+TTTTTTTTTTTTTTTTTTTTTTTTG
=====================================
tests/integration_tests/kisspliceDBGTest.py
=====================================
@@ -1,54 +1,34 @@
#!/usr/bin/env python3
-import re
from sys import argv
from os.path import dirname, abspath
-import ProcessLauncher
-TEST_INSTDIR=dirname(abspath(argv[0]))
-command_line = argv[1]+"/kissplice -k 25 -M 1000 -r "+TEST_INSTDIR+"/data/HBM75brain_100000.fasta -r "+TEST_INSTDIR+"/data/HBM75liver_100000.fasta --keep-counts -o "+ TEST_INSTDIR +"/results"
-result = ProcessLauncher.run(command_line)
+import filecmp
+import shutil
+import shlex
+import subprocess
-i=2
-for fn in [TEST_INSTDIR + "/results/graph_HBM75brain_100000_HBM75liver_100000_k25.edges", TEST_INSTDIR + "/results/graph_HBM75brain_100000_HBM75liver_100000_k25.nodes", TEST_INSTDIR + "/results/graph_HBM75brain_100000_HBM75liver_100000_k25.abundance"]:
- fo=fn+"0"
- f=open(fn,"r")
- o=open(fo,"w")
- toWrite=[]
- for line in f:
- line=line.strip()
- lLine=line.split()
- toWrite.append(lLine[i])
- toWrite.sort()
- o.write("\n".join(toWrite))
- i-=1
- f.close()
- o.close()
+TEST_INSTDIR = dirname(abspath(argv[0]))
+BINDIR = argv[1]
-command_line_diff_edges = "diff " + TEST_INSTDIR + "/results/graph_HBM75brain_100000_HBM75liver_100000_k25.edges0 " + TEST_INSTDIR + "/data/graph_HBM75brain_100000_HBM75liver_100000_k25.edges0"
-result_diff_edges = ProcessLauncher.run(command_line_diff_edges)
+result = subprocess.run(
+ shlex.split(f"{BINDIR}/kissplice -k 25 -M 1000 -r {TEST_INSTDIR}/data/HBM75brain_100000.fasta -r {TEST_INSTDIR}/data/HBM75liver_100000.fasta --keep-counts -o {TEST_INSTDIR}/results"),
+ check = True, capture_output = True
+)
+assert result.stderr == b""
-command_line_diff_nodes = "diff " + TEST_INSTDIR + "/results/graph_HBM75brain_100000_HBM75liver_100000_k25.nodes0 " + TEST_INSTDIR + "/data/graph_HBM75brain_100000_HBM75liver_100000_k25.nodes0"
-result_diff_nodes = ProcessLauncher.run(command_line_diff_nodes)
-
-command_line_diff_counts = "diff " + TEST_INSTDIR + "/results/graph_HBM75brain_100000_HBM75liver_100000_k25.abundance0 " + TEST_INSTDIR + "/data/graph_HBM75brain_100000_HBM75liver_100000_k25.abundance0"
-result_diff_counts = ProcessLauncher.run(command_line_diff_counts)
+# Extract entries at column_index, sort them and store them in file+"0"
+def copy_sorted_entries(column_index: int, file: str):
+ with open(file) as lines:
+ entries = [l.strip().split()[column_index] for l in lines]
+ entries.sort()
+ with open(file + "0", "w") as f:
+ f.writelines(entry + "\n" for entry in entries)
-# testing expected results
-successful = True
+copy_sorted_entries(2, f"{TEST_INSTDIR}/results/graph_HBM75brain_100000_HBM75liver_100000_k25.edges")
+copy_sorted_entries(1, f"{TEST_INSTDIR}/results/graph_HBM75brain_100000_HBM75liver_100000_k25.nodes")
+copy_sorted_entries(0, f"{TEST_INSTDIR}/results/graph_HBM75brain_100000_HBM75liver_100000_k25.abundance")
-if result_diff_edges.decode('utf8') != "":
- successful = False
-
-if result_diff_nodes.decode('utf8') != "":
- successful = False
-
-if result_diff_counts.decode('utf8') != "":
- successful = False
-
-# summary
-if successful:
- print("kisspliceDBGTest.py: test SUCCESSFUL")
-else:
- print("kisspliceDBGTest.py: test FAILED")
+assert filecmp.cmp(f"{TEST_INSTDIR}/results/graph_HBM75brain_100000_HBM75liver_100000_k25.edges0", f"{TEST_INSTDIR}/data/graph_HBM75brain_100000_HBM75liver_100000_k25.edges0")
+assert filecmp.cmp(f"{TEST_INSTDIR}/results/graph_HBM75brain_100000_HBM75liver_100000_k25.nodes0", f"{TEST_INSTDIR}/data/graph_HBM75brain_100000_HBM75liver_100000_k25.nodes0")
+assert filecmp.cmp(f"{TEST_INSTDIR}/results/graph_HBM75brain_100000_HBM75liver_100000_k25.abundance0", f"{TEST_INSTDIR}/data/graph_HBM75brain_100000_HBM75liver_100000_k25.abundance0")
-command_line_rm = "rm -r "+TEST_INSTDIR +"/results"
-ProcessLauncher.run(command_line_rm)
\ No newline at end of file
+shutil.rmtree(f"{TEST_INSTDIR}/results")
\ No newline at end of file
=====================================
tests/integration_tests/kisspliceGraphAndSequenceTest.py
=====================================
@@ -10,10 +10,10 @@ print(result)
# testing expected results
successful = True
-if not (re.search('0a: Single SNPs, Inexact Repeats or sequencing substitution errors, 29 found', result.decode())):
+if not (re.search('0a: Single SNPs, Inexact Repeats or sequencing substitution errors, 19 found', result.decode())):
successful = False
-if not (re.search('0b: Multiple SNPs, Inexact Repeats or sequencing substitution errors, 8 found', result.decode())):
+if not (re.search('0b: Multiple SNPs, Inexact Repeats or sequencing substitution errors, 1 found', result.decode())):
successful = False
if not (re.search('1: Alternative Splicing Events, 0 found', result.decode())):
=====================================
tests/integration_tests/kisspliceTwoSequencesTest.py
=====================================
@@ -10,10 +10,10 @@ print(result)
# testing expected results
successful = True
-if not (re.search('0a: Single SNPs, Inexact Repeats or sequencing substitution errors, 81 found', result.decode())):
+if not (re.search('0a: Single SNPs, Inexact Repeats or sequencing substitution errors, 69 found', result.decode())):
successful = False
-if not (re.search('0b: Multiple SNPs, Inexact Repeats or sequencing substitution errors, 17 found', result.decode())):
+if not (re.search('0b: Multiple SNPs, Inexact Repeats or sequencing substitution errors, 8 found', result.decode())):
successful = False
if not (re.search('1: Alternative Splicing Events, 1 found', result.decode())):
=====================================
thirdparty/kissreads/include/coherence_algorithm.h
=====================================
@@ -44,7 +44,7 @@
#define COHERENCE_ALGORITHM_H_
-char read_coherent_generic (const int pwi, const char * fragment, const char * read, const int subst_allowed);
+char read_coherent_generic (const int pwi, const char * fragment, const char * read, const int subst_allowed, const int modIndel);
char read_coherent_SNP(const int pwi, const char * fragment, const char * read, const int subst_allowed);
#endif /* COHERENCE_ALGORITHM_H_ */
=====================================
thirdparty/kissreads/src/coherent_algorithm.cpp
=====================================
@@ -54,7 +54,7 @@
* Tests if the overlapping part between read and fragment do not have more than <code>subst_allowed</code> substitions
* returns 1 if true between read and fragment, 0 else
*/
-char read_coherent_generic(const int pwi, const char * fragment, const char * read, const int subst_allowed){
+char read_coherent_generic(const int pwi, const char * fragment, const char * read, const int subst_allowed, const int modIndel){
// printf("read coherent, subst_allowed = %d\n", subst_allowed);
int substitution_seen=0; // number of seen substitutions for now
@@ -86,14 +86,15 @@ char read_coherent_generic(const int pwi, const char * fragment, const char * re
}
-
// walk the read and the fragment together, detecting substitutions.
// stop if the number of substitution is too high
while(fragment[pos_on_fragment]!='\0' && read[pos_on_read]!='\0'){
//if(fragment[pos_on_fragment]!=read[pos_on_read]) && fragment[pos_on_fragment]!='*') {// one subsitution
- if(fragment[pos_on_fragment]!=read[pos_on_read] && fragment[pos_on_fragment]!='*' && fragment[pos_on_fragment]!='?' && fragment[pos_on_fragment]!='N'){ // one subsitution
+ if(fragment[pos_on_fragment]!=read[pos_on_read] && fragment[pos_on_fragment]!='*' && fragment[pos_on_fragment]!='?' && fragment[pos_on_fragment]!='N' && fragment[pos_on_fragment]!='I'){ // one subsitution
substitution_seen++;
if(substitution_seen>subst_allowed) break; // too much subsitutions
+ } else if(modIndel==1 && fragment[pos_on_fragment]!=read[pos_on_read] && fragment[pos_on_fragment]=='I') {
+ pos_on_read--;
}
pos_on_fragment++;
pos_on_read++;
=====================================
thirdparty/kissreads/src/extension_algorithm.cpp
=====================================
@@ -483,10 +483,16 @@ float read_coherence (gzFile reads_file,
read_coherence=0;
- if(no_subsutitution_on_central_position)
+ if(no_subsutitution_on_central_position){
read_coherence = read_coherent_SNP(pwi, starter, read, subst_allowed);
- else
- read_coherence = read_coherent_generic(pwi, starter, read, subst_allowed);
+ }
+ else {
+ read_coherence = read_coherent_generic(pwi, starter, read, subst_allowed, 0); // Try alignment with 'I' as deletion on fragment
+ if(read_coherence != 1) {
+ read_coherence = read_coherent_generic(pwi, starter, read, subst_allowed, 1); // Try alignment with 'I' as insertion on fragment
+ }
+ }
+
if(read_coherence == 1){ // tuple read starter position is read coherent
=====================================
utils/cmake/cmake_uninstall.cmake.in deleted
=====================================
@@ -1,22 +0,0 @@
-if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
- message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
-endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
-
-file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
-string(REGEX REPLACE "\n" ";" files "${files}")
-list(REVERSE files)
-foreach (file ${files})
- message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
- if (EXISTS "$ENV{DESTDIR}${file}")
- execute_process(
- COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}"
- OUTPUT_VARIABLE rm_out
- RESULT_VARIABLE rm_retval
- )
- if(NOT ${rm_retval} EQUAL 0)
- message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
- endif (NOT ${rm_retval} EQUAL 0)
- else (EXISTS "$ENV{DESTDIR}${file}")
- message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
- endif (EXISTS "$ENV{DESTDIR}${file}")
-endforeach(file)
View it on GitLab: https://salsa.debian.org/med-team/kissplice/-/compare/6ba8b768222490f02a6b81560e6e7d3ec7cc9833...9a0a1a18d2b320f068af0ff2175e9736a2f90a7d
--
View it on GitLab: https://salsa.debian.org/med-team/kissplice/-/compare/6ba8b768222490f02a6b81560e6e7d3ec7cc9833...9a0a1a18d2b320f068af0ff2175e9736a2f90a7d
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20220630/5fa82d3a/attachment-0001.htm>
More information about the debian-med-commit
mailing list