We are using PMD Copy Paste Detector (CPD) to analyze our C and C++ code.
However, there are a few parts of the code that are very similar, but with a good reason and we would like to suppress the warnings for these parts.
The documentation of PMD CPD only mentions something about annotations, but this will not work for our these languages.
How can I still ignore warnings for specific parts?
Is there a comment to do so perhaps?
[UPDATE] I'm using the following Groovy script to run CPD:
#GrabResolver(name = 'jcenter', root = 'https://jcenter.bintray.com/')
#Grab('net.sourceforge.pmd:pmd-core:5.4.+')
#Grab('net.sourceforge.pmd:pmd-cpp:5.4.+')
import net.sourceforge.pmd.cpd.CPD
import net.sourceforge.pmd.cpd.CPDConfiguration
import java.util.regex.Pattern
def tokens = 60
def scanDirs = ['./path/to/scan', './scan/this/too']
def ignores = [
'./ignore/this/path',
'./this/must/be/ignored/too'
].collect({ it.replace('/', File.separator) })
def rootDir = new File('.')
def outputDir = new File('./reports/analysis/')
def filename_date_format = 'yyyyMMdd'
def encoding = System.getProperty('file.encoding')
def language_converter = new CPDConfiguration.LanguageConverter()
def config = new CPDConfiguration()
config.language = new CPDConfiguration.LanguageConverter().convert('c')
config.minimumTileSize = tokens
config.renderer = config.getRendererFromString 'xml', 'UTF-8'
config.skipBlocksPattern = '//DUPSTOP|//DUPSTART'
config.skipLexicalErrors = true
def cpd = new CPD(config)
scanDirs.each { path ->
def dir = new File(path);
dir.eachFileRecurse(groovy.io.FileType.FILES) {
// Ignore file?
def doIgnore = false
ignores.each { ignore ->
if(it.path.startsWith(ignore)) {
doIgnore = true
}
}
if(doIgnore) {
return
}
// Other checks
def lowerCaseName = it.name.toLowerCase()
if(lowerCaseName.endsWith('.c') || lowerCaseName.endsWith('.cpp') || lowerCaseName.endsWith('.h')) {
cpd.add it
}
}
}
cpd.go();
def duplicationFound = cpd.matches.hasNext()
def now = new Date().format(filename_date_format)
def outputFile = new File(outputDir.canonicalFile, "cpd_report_${now}.xml")
println "Saving report to ${outputFile.absolutePath}"
def absoluteRootDir = rootDir.canonicalPath
if(absoluteRootDir[-1] != File.separator) {
absoluteRootDir += File.separator
}
outputFile.parentFile.mkdirs()
def xmlOutput = config.renderer.render(cpd.matches);
if(duplicationFound) {
def filePattern = "(<file\\s+line=\"\\d+\"\\s+path=\")${Pattern.quote(absoluteRootDir)}([^\"]+\"\\s*/>)"
xmlOutput = xmlOutput.replaceAll(filePattern, '$1$2')
} else {
println 'No duplication found.'
}
outputFile.write xmlOutput
You can define your custom markers for excluding certain blocks from analysis through the --skip-blocks-pattern option.
--skip-blocks-pattern Pattern to find the blocks to skip. Start and End pattern separated by |. Default is #if 0|#endif.
For example the following will ignore blocks between /* SUPPRESS CPD START */ and /* SUPPRESS CPD END */ comments (the comment must occupy a separate line):
$ ./run.sh cpd --minimum-tokens 100 --files /path/to/c/source --language cpp ----skip-blocks-pattern '/* SUPPRESS CPD START */|/* SUPPRESS CPD END */'
Note however, that this will cause the tool perform copy-paste-detection inside code delimited by #if 0/#endif.
After searching through the code of PMD on GitHub, I think I can safely say that this is NOT supported at this point in time (current version being PMD 5.5.0).
A search for CPD-START in their repository, does not show any results within the pmd-cpp directory (see the search results on GitHub).
I know this is a ~3 years old question, but for completeness, CPD started supporting this in PMD 5.6.0 (April 2017) in Java, and since 6.3.0 (April 2018) it has been extended to many other languages such as C/C++. Nowadays, almost all CPD supported languages allow for comment-based suppressions.
The complete (current) docs for comment-based suppression are available at https://pmd.github.io/pmd-6.13.0/pmd_userdocs_cpd.html#suppression
It's worth noting, if a file has a // CPD-OFF comment, but no matching // CPD-ON, everything will be ignored until the end of file.
I don't have any help for CPD. In general, I know about such tools; I don't understand the bit about "warnings".
Our CloneDR tool finds exact and near-miss duplicate code. IMHO, it finds better clones than CPD, because it uses the language syntax/ structure as a guide. [This fact is backed up by a research report done by a third party that you can find at the site]. And it does not issue "warnings".
If there is code that it thinks is involved in a clone, the tool will generate an output report page for the clones involved. But that isn't a warning. There is no way to suppress the reporting behavior. Obviously, if you have seen such a clone and decide it is not interesting, you can mark one of the clone entries with a comment stating that it is an uninteresting clone; that comment will show up in the clone report. (Such) comments have no impact whatsover on what clones are detected by CloneDR, so adding them does not change the computed answer.
Related
A very common question on StackOverflow with regards to C++/R or C/R package integration is regarding the error in dyn.load(), e.g.
> ## within R
> Error in .Call("function_c") : C symbol name "function_c" not in load table
whereby function_c is some function in C like
SEXP function_c() {
Rprintf("Hello World!\n"); // manually changed
return(R_NilValue);
}
This error come sup due to many types of mistakes, e.g. incorrect compliation, misnamed functions, the user didn't use extern "C" for Cpp code, etc.
Question: Is there any way to view all "available" objects which the user could load via dyn.load() after compilation?
How about the following? I'm not sure it covers everything, but it should be close:
# manipulate search() to get all loaded packages
loadedPkgs = grep('^package:', search(), value = TRUE)
loadedPkgs = gsub('package:', '', loadedPkgs, fixed = TRUE)
# add names here to make the results of lapply pretty
names(loadedPkgs) = loadedPkgs
allCRoutines = lapply(loadedPkgs, function(pkg) {
# see: https://stackoverflow.com/questions/8696158/
pkg_env = asNamespace(pkg)
# this works at a glance
check_CRoutine = function(vname) {
'CallRoutine' %in% attr(get(vname, envir = pkg_env), 'class')
}
names(which(sapply(ls(envir = pkg_env, all = TRUE), check_CRoutine)))
})
The object is a bit long, so I'll just show for one package:
allCRoutines[['utils']]
# $utils
# [1] "C_crc64" "C_flushconsole" "C_menu" "C_nsl" "C_objectSize" "C_octsize" "C_processevents"
# [8] "C_sockclose" "C_sockconnect" "C_socklisten" "C_sockopen" "C_sockread" "C_sockwrite"
What I'm not sure of is that check_CRoutine catches everything we'd consider as relevant to your question. I'm also not sure this covers your main interest (whether these objects can succesfully be fed to dyn.load); perhaps the routines returned here could be passed to dyn.load with a try wrapper?
With the rack-timeout gem installed how is it possible to display ERROR only related logs? For example I would like to avoid having the below in my logs:
source=rack-timeout id=8a11a8ac3dadb59a4f347d8e365faddf timeout=20000ms service=0ms state=active
source=rack-timeout id=8a11a8ac3dadb59a4f347d8e365faddf timeout=20000ms service=49ms state=completed
source=rack-timeout id=ee947d4a291d02821ab108c4c127f555 timeout=20000ms state=ready
The following did not work:
Rack::Timeout.unregister_state_change_observer(:active)
The below may be on the right path but I'm having trouble testing:
Rack::Timeout::Logger.level = Logger::ERROR
(Note the class name was changed from Stage… to State… in v0.3.0)
In production I want to log at INFO level so I get a log message per request, but I don't want this noise from rack-timeout.
You can alter the STATE_LOG_LEVEL hash in the StateChangeLoggingObserver and change the log level used for the different states. I use this in my initialiser to prevent the ready and completed logs from showing:
Rack::Timeout::StateChangeLoggingObserver::STATE_LOG_LEVEL[:ready] = :debug
Rack::Timeout::StateChangeLoggingObserver::STATE_LOG_LEVEL[:completed] = :debug
My solution to this problem was to give rack-timeout its own logger.
Once you've done that, you can change its log level:
# config/initializers/timeout.rb
Rack::Timeout::Logger.logger = Logger.new("log/timeout.log")
Rack::Timeout::Logger.logger.level = Logger::ERROR
In config/initializers/rack_timeout.rb I added:
Rack::Timeout::Logger.disable if Rails.env.development?
More detailed options are described here: https://github.com/sharpstone/rack-timeout/blob/master/doc/logging.md
(Thanks #Sandip Subedi for the suggestion)
Read here for more information
https://github.com/heroku/rack-timeout#rails-apps-manually
or
https://github.com/heroku/rack-timeout/blob/master/doc/settings.md
You could also try this code, though untested.
Rack::Timeout::StageChangeLoggingObserver.logger = logger = ::Logger.new(STDERR)
logger.level = ::Logger::DEBUG
logger.formatter = ->(severity, timestamp, progname, msg) {"[#{timestamp}] #{msg} at=#{severity.downcase}\n" }
I'm making my first steps in the Jenkins workflow (Jenkins ver 1.609.1)
I need to read a file, line by line, and then run regex on each line.
I'm interested in the regex "grouping" type, however "project" and "status" variables (the code below) get null value in Jenkins . Any suggestions what is wrong and how to fix it ?
def line = readFile (file)
def resultList = line.tokenize()
for(item in resultList ){
(item =~ /(\w+)=(\w+)$/).each { whole, project, status ->
println (whole)
println (project)
println (status)
}
}
each with a closure will not work: JENKINS-26481
If something using advanced language features works in standalone Groovy but not in Workflow, try just encapsulating it in a method marked #NonCPS. This will effectively run it as a “native” method. Only do this for code you are sure will run quickly and not block on I/O, since it will not be able to survive a Jenkins restart.
Well,
After checking out some other regex options I came around with the following solution that seems working :
def matcher = item =~ /(?<proj>\w+)=(?<status>\w+)/
if( matcher.matches() ) { etc...}
My main goal is to add support of -isystem include paths in scons, like this is proposed here : https://stackoverflow.com/a/2547261/4042960
The solution of creating new variables works fine: I do that:
#### Add support for system headers
env['SYSTEMINCPREFIX'] = '-isystem '
env['SYSTEMINCSUFFIX'] = ''
env['_CPPSYSTEMINCFLAGS'] = '$( ${_concat(SYSTEMINCPREFIX, CPPSYSTEMPATH, SYSTEMINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
env['_CCCOMCOM'] += ' $_CPPSYSTEMINCFLAGS'
I use it by adding for instance:
env.Append(CPPSYSTEMPATH = ['/my/include/path'])
My problem is that now, the path /my/include/path is not scanned by the C (or C++) dependency scanner. After many search, I failed to find how to add my variable "CPPSYSTEMPATH" to be treated like "CPPPATH" by the dependency scanner.
Does anyone know how I could add the search path contained in "CPPSYSTEMPATH" to the existing C scanner ?
I hope that my problem is clear enough, else do not hesitate to tell me.
Here's a basic recipe for replacing the FindPath method of the default C scanner, but be warned it's an ugly hack:
# Create environment
env = Environment()
# Define your new env variable as combination of both paths
env['MYCPPPATHS'] = ['$CPPPATH','$CPPSYSTEMPATH']
# Replace the path_function of the standard C scanner by:
import SCons.Tool
import SCons.Scanner
setattr(SCons.Tool.CScanner,'path_function',SCons.Scanner.FindPathDirs('MYCPPPATHS'))
# Do your build stuff...
env['CPPSYSTEMPATH'] = 'myinclude'
env.Program('main','main.cpp')
By the way, why not ask these kind of questions on our user mailing list scons-users#scons.org? ;)
I am having trouble using the ABPersonViewController with RubyMotion. The error I'm getting is
Objective-C stub for message setDisplayedPerson:' typev#:^v' not precompiled. Make sure you properly link with the framework or library that defines this message.
I suspect this is due to RubyMotion not casting to the type IOS expects. I think ABPersonCreate() is returning a CFType but the displayedPerson setter is expecting it to be cast as a ABRecordRef (that's just a guess from the error messages)
Here's the sample code to see the problem (based on Apple's QuickContacts sample):
#Rakefile
$:.unshift("/Library/RubyMotion/lib")
require 'motion/project'
Motion::Project::App.setup do |app|
# Use `rake config' to see complete project settings.
app.name = 'contacts'
app.frameworks += ['AddressBook', 'AddressBookUI']
end
and
# app/app_delegate.rb
class AppDelegate
def application(application, didFinishLaunchingWithOptions:launchOptions)
window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.applicationFrame)
window.rootViewController = UINavigationController.alloc.init
window.rootViewController.wantsFullScreenLayout = true
window.makeKeyAndVisible
true
# This works
add_person('Alex', 'Rothenberg')
# This fails (is it a type casting problem?)
show_person_view_controller('Rothenberg')
end
def show_person_view_controller(name)
anError = nil
address_book = ABAddressBookCreate();
people = ABAddressBookCopyPeopleWithName(address_book, name);
person = people.first
picker = ABPersonViewController.alloc.init.autorelease
picker.personViewDelegate = self
puts "Should this be an AddressBookRef? #{person.inspect}" # => #<__NSCFType:0x8c3bec0>
picker.displayedPerson = person
# The previous line fails
puts "We never reach this line!"
self.navigationController.pushViewController(picker, animated:true)
end
def add_person(first_name, last_name)
error = nil
contact = ABPersonCreate()
ABRecordSetValue( contact, KABPersonFirstNameProperty, first_name, error )
ABRecordSetValue( contact, KABPersonLastNameProperty, last_name, error )
address_book = ABAddressBookCreate()
ABAddressBookAddRecord( address_book, contact, error )
ABAddressBookSave( address_book, error )
end
end
When you run it we are able to add to the address book in the add_person method but it fails in show_person_view_controller on the line picker.displayedPerson = person
$ rake
Build ./build/iPhoneSimulator-5.1-Development
Simulate ./build/iPhoneSimulator-5.1-Development/contacts.app
Should this be an AddressBookRef? #<__NSCFType:0x8da2900>
Objective-C stub for message `setDisplayedPerson:' type `v#:^v' not precompiled. Make sure you properly link with the framework or library that defines this message.
Any suggestions would be appreciated
This will work properly as of RubyMotion 1.12 (run sudo motion update).
Fixed a bug where performing Objective-C methods that accept
CFType objects
would crash the program (ex. [ABPersonViewController setDisplayedPerson:]).