Compensating for GC bias is only needed if you have GC bias between samples or groups. Likewise, trying to compensate for length distribution is only needed if it shows a significant bias according to sample or group. With CQN, there are a few diagnostic plots that you can learn how to make in the vignette (e.g. an MA-plot with points colored by GC content) that will let you know if there's notable GC bias. In my experience (so take this with the appropriate grain of salt) this is rarely useful. I recall only having one dataset that showed significant per-sample GC-bias (CQN nicely got rid of that).
BTW, you mention length normalization as well. I've actually never seen a length bias in a dataset, though I suppose it could occur. For calculating length and GC content, one would normally use a "union gene model", wherein overlapping transcripts are merged, such that overlapping portions are only counted once. The following R script will read in a GTF file and genomic reference in fasta format and produce per-gene length and GC content calculations in an appropriate manner. The output (GC_lengths.tsv) can be used in CQN. I wrote this for my own needs, so the GTF and fasta filenames are hard-coded (just change them).
#!/usr/bin/Rscript
library(GenomicRanges)
library(rtracklayer)
library(Rsamtools)
#Change the next two lines!!!
GTFfile = "/home/ryand/Documents/Misc/Mus_musculus/Ensembl/GRCm38.71/Annotation/Mus_musculus.GRCm38.71.gtf" #CHANGE ME!!!
FASTAfile = "/home/ryand/Documents/Misc/Mus_musculus/Ensembl/GRCm38.71/Sequence/Mus_musculus.GRCm38.71.dna.toplevel.fa" #CHANGE ME!!!
#Load the annotation and reduce it
GTF <- import.gff(GTFfile, format="gtf", genome="GRCm38.71", asRangedData=F, feature.type="exon")
grl <- reduce(split(GTF, elementMetadata(GTF)$gene_id))
reducedGTF <- unlist(grl, use.names=T)
elementMetadata(reducedGTF)$gene_id <- rep(names(grl), elementLengths(grl))
#Open the fasta file
FASTA <- FaFile(FASTAfile)
open(FASTA)
#Add the GC numbers
elementMetadata(reducedGTF)$nGCs <- letterFrequency(getSeq(FASTA, reducedGTF), "GC")[,1]
elementMetadata(reducedGTF)$widths <- width(reducedGTF)
#Create a list of the ensembl_id/GC/length
calc_GC_length <- function(x) {
nGCs = sum(elementMetadata(x)$nGCs)
width = sum(elementMetadata(x)$widths)
c(width, nGCs/width)
}
output <- t(sapply(split(reducedGTF, elementMetadata(reducedGTF)$gene_id), calc_GC_length))
colnames(output) <- c("Length", "GC")
write.table(output, file="GC_lengths.tsv", sep="\t")
I guess I should reply to the ERCC spike-in part of your question since you linked to a comment from me :p
ERCC spike-ins are useful only for library size normalization. CQN is meant to account for GC or other biases. Because of that, the two can be compatible. Having said that, remember that ERCC spike-ins are only beneficial in very particular circumstances (e.g., when performing single-cell sequencing or when you suspect gross changes in transcription levels). Most people won't benefit from spike-ins.