Replace word in package names and variables in code - android

In my android project I got very intericting task
My company wants to hide all mintions about her in code (variables names, packages and etc)
But only for one flavour, so I cannot do it once for all project.
My first idea, was writing a simple gradle task, that will replace all strings that fit in code, but in this case package names will remain unchanged.
Secondly, since we have ci on Jenkins, I thought about jenkins script, that will rename all files and its content, if it has keyword. But this solution looks very bulky for me.
Maybe there is another, elegant way?

Replacing the package name/variable names blindly seems a bit risky, as it could replace other overlapping strings as well, which may lead to various issues. Assuming your package name is unique and you don't have any overlapping names and it doesn't result in any directory name changes, you can use different methods to replace the package name.
Option 1
Using shell to achieve this. There are plenty of different ways to do this, following is one option with grep and sed
sh '''
grep -rl ${PACKAGE_NAME_TO_REPLACE} ${DESTINATION_DIR} | xargs sed -i "s&${PACKAGE_NAME_TO_REPLACE}&${PACKAGE_NAME_NEW}&g"
'''
You can take a look at this and this to understand different methods you can use.
Option 2
If you want a more controlled approach, you can achieve this with some groovy code as well. Simply run the following within your Pipeline.
def dirToSearchIn = "/where/to/replace"
// Change the content on specific files. You can improve the regex pattern below to fine-tune it. With the following pattern only files with extensions .java and .md will be changed.
def filterFilePattern = ~/.*\.java|.*\.md$/
def oldString = "replaceme"
def newString = "newme"
new File(dirToSearchIn).traverse(type: groovy.io.FileType.FILES, nameFilter: filterFilePattern) { file ->
println "Processing file: " + file.getPath()
def fileContent = file.text;
if (fileContent.contains(oldString)) {
println "Replacing the content of the file: " + file.getPath()
file.write(fileContent.replaceAll(oldString, newString));
} else {
println "Skipping file: " + file.getPath()
}
}

Adding something like the following to your top-level build.gradle file should do the trick (assuming your company is called “ACME” and you rather want it to be called “foobar”):
def duplicateProjDirName = 'duplicateProj'
def duplicateProjDir = project.layout.buildDirectory.dir(duplicateProjDirName)
def duplicateProj = tasks.register('createDuplicateProject', Copy) {
enabled = (projectDir.name != duplicateProjDirName)
from(project.layout.projectDirectory)
into(duplicateProjDir)
exclude('build', '.gradle')
def acmePattern = /(?i)acme/
def newCompanyName = 'foobar'
eachFile { it.path = it.sourcePath.replaceAll(acmePattern, newCompanyName) }
filter { it.replaceAll(acmePattern, newCompanyName) }
includeEmptyDirs = false
}
def duplicateBuild = tasks.register('buildDuplicateProject', GradleBuild) {
enabled = (projectDir.name != duplicateProjDirName)
dependsOn(duplicateProj)
dir = duplicateProjDir
tasks = ['build']
}
tasks.named('build').configure {
dependsOn(duplicateBuild)
}
This essentially adds two tasks to the project:
createDuplicateProject duplicates the project under build/duplicateProj/ with all mentions of “ACME” replaced with “foobar”. It also takes care of renaming files/directories (in contrast to the solutions in other answers so far).
buildDuplicateProject builds the duplicate project.
While this may work in basic scenarios (I’ve successfully tested it with a small dummy Java project and Gradle 7.6), there are some edge cases to think about:
There may be dependencies (libraries, services, etc.) which contain the company name and which won’t work anymore after they’ve been renamed.
This way of replacing
may not work well for binary files.
does not catch occurrences in code such as "AC" + "ME".
case-insensitively may lead to weird-looking names that don’t follow common conventions. In the worst case, this could lead to different behavior, too.
There may be downstream projects which depend on package names or the like that are renamed here.
Your company may not only be identifiable by name, e.g., there may also be logos in image files, etc.
and probably others

Related

Gradle: detect if there are #Test methods in a particular folder

I would like to create a gradle method that would return me a boolean saying if a project passed as input has some tests defined in a particular sub folder. Particularly, I am interested in knowing if the project contains android instrumentation tests.
So far, this is what I have (my root project has multiple sub-projects):
def doesProjectContainsInstrumentationTests(project) {
def rootDir = new File(rootProject.rootDir, "")
def projectPath = project.getPath().replaceAll(':', '/')
def fullPath = new File(rootDir, projectPath)
def androidTestFolder = new File(fullPath, "src/androidTest")
//TODO: find a better way to detect if the module has instrumentation tests to run (using some annotation like #Test or #RunWith?)
def containsInstrumentationTests = androidTestFolder.exists()
return containsInstrumentationTests
}
The problem with this implementation is that the folder might exist but no tests are in it. In that situation, the code above would return true while it should return false.
Ideally I would like to reuse the same logic described here, but not sure I can do it.
Any suggestion on how I could achieve that? I'm ok if there is a completly different way to achieve that, as long as it works :)

Cucumber steps implementation in kotlin isn't recognized in feature file

I have this feature file :
Given I launch google using chrome as browser
When I search by kotlin
Then I will see 32 results per page
where my properties file has the content :
e1=www.google.com
field= kotlin
nr=32
However, the third steps from my feature file appears as if they aren't implemented : https://www.screencast.com/t/fP9vjdToSI
I have implemented the steps in a kotlin class :
init {
Given("^I launch \"(.*)\" using chrome as browser$") { element: String ->
// load the properties file
// prop.load(file)
prop.load(StepsDefinition::class.java!!.getClassLoader()
.getResourceAsStream("application-test.properties"))
// set the properties
prop.getProperty("el1")
val element= prop.getProperty("el1")
println(prop.getProperty("el1"))
driver.get(element)
When("^I search by \"(.*)\"$") { field: String ->
val button = driver.findElement(By.xpath("//input[#value=\"" + field+ "\"]" +
"| //button[contains(text(),\"" + field+ "\")]"))
button.isDisplayed
button.click()
}
Then("^I see \"(.*)\" results per page") { nr: String ->
val wait = WebDriverWait(driver, 10)
Assert.assertTrue(driver.getTitle().contains(nr))
}
Can someone please help me?
Thanks.
I don't have cucumber currently set up on my machine so I can't really verify what I'm saying, however I'd say it's just a mismatch between the step definition and the step in the feature.
Your step definition expects the keyword to be wrapped in quotes, while in your feature file you're not using any quotes. The same should apply to the When step (same error) and to the Then step, where you put an extra will.

What is the purpose of using double underscore ( __ ) before the start and after the end of an file name in c?

I'm studying the android kernel as a beginner. I can read the messages thrown from the macro ERROR() inside the function main() at system/core/init/init.c using dmesg command through adb. I observed that after calling the function open_devnull_stdio() inside main(), dmesg no longer displays the messages thrown by ERROR().
To find the reason, I started digging into the declaration of open_devnull_stdio() inside system/core/init/util.c and I found this line I can't understand
static const char *name = "/dev/__null__";
Actually there was no file named __null__ inside /dev/ in the device, but there was a file named null and I was able to grab it using adb pull and it was a 0 byte (empty) file.
So why is a file name wrapped with double underscore (__) ?
Here is the link for the util.c
There is no special purpose of using double underscore before the start, after the end or both in C. From the point of view of C the file name is just a string, the operating system is free to interpret in whatever way it chooses. From the point of view of Linux, the same applies. Underscores in file names are just characters. They are not treated differently from the letters b and t.
If I guessed right and I'm reading the same file as you (it might be a good idea to link to the source code you're reading) then it should be pretty obvious what the code does on the lines after the one you mentioned. The next lines are:
if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
fd = open(name, O_RDWR);
unlink(name);
Which creates the null device which is then opened and immediately deleted again.
I suspect this is done so that programs can run without access to the root filesystem and still be able to open the equivalent of /dev/null.
I don't know the answer but I have an idea:
The following page shows an "strace" output where /dev/__null__ is used:
https://gist.github.com/tetsu-koba/1522515
Under Linux device files have a 33-bit (?) number which identifies the device. (At least under old Linux versions) you could delete some file in /dev and you could restore it or even create it in another directory (!) when you know the 33-bit number! (So you can delete the device /dev/sda2 and create the device (not file!) /home/myuser/sda2 instead.)
The trace in the link above shows the following three lines:
mknod("/dev/__null__", S_IFCHR|0600, makedev(1, 3)) = 0
open("/dev/__null__", O_RDWR|O_LARGEFILE) = 3
unlink("/dev/__null__") = 0
These lines will create the device file /dev/__null__ (with the 33-bit number identifying /dev/null). Then it opens that file and then it removes the file again.
Maybe this is done because the tool shall be able to run both on Linux installations where the device file "/dev/null" is present (in this case the file should not be overwritten) and on installations where that file is missing (in this case a replacement file must be created using the known 33-bit number).
As other people have pointed out this just tells it's the "null device", not a regular file called "null". null is supposed to act like an information sink, not like a normal file where you dump your data to. Hope this helps.

error with pymtp to work on python 3

I want to access a android device from python to download some photos.
libmtp works from the CLI.
Than pymtp. It's been around for a while but it's designed for python 2 and i'm using python 3. Meanwhile fixed several minor issues but i'm stuck at an error from function get_filelisting
specially this section:
ret = []
next = files
while next:
ret.append(next.contents)
if (next(next.contents) is None):
break
next = next(next.contents)
The error is related to the "next".
That section looks strange to me, i've been coding in python for a while but i'm new to ctypes. Tried a lot of variants, they all failed. The "next" could be confusing with python buildin function so i renamed it to nextpointer and came to this code:
ret = []
nextpointer = files
while nextpointer:
ret.append(nextpointer.contents)
nextpointer = nextpointer.contents.next
It seems to work but did it work by accident ? does it have any design flaws ? Could anyone with experience on python ctypes confirm this a solution ? Any suggestion welcome.
From python2.7 documentation
next(iterator[, default])
Retrieve the next item from the iterator by calling its next() method. If default is given, it is returned if the iterator is
exhausted, otherwise StopIteration is raised.
from python3 documentation
next(iterator[, default])
Retrieve the next item from the iterator by calling its __next__() method. If default is given, it is returned if the iterator is
exhausted, otherwise StopIteration is raised.
Notice that next() method was removed from python3 but the function still exists.
This is all I can say about the next function and .next()/__next__() methods.
I downloaded the pymtp module and get_filelisting() is slightly different from what you posted in your ported code, here it is:
ret = []
next = files
while next:
ret.append(next.contents)
if (next.contents.next == None):
break
next = next.contents.next
If none of this helped you (which probably didn't :D), the version of pymtp library that I am using is 0.0.6 download using pip.

How can I import XML schema without network access?

I'm implementing schema validation using libxml2. The schema I'm validating against imports two other schemas with lines like:
<xs:import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.somewebsite.com/xsd/xml.xsd"/>
All three schema files are located in the same directory on the device.
This works well when the device has internet access, but fails when it does not, as libxml2 still attempts to download the imported schemas from the schemaLocation even though I'm passing in XML_PARSE_NONET.
I tried getting libxml2 to load the files locally by editing the schemaLocation attribute to xml.xsd, ./xml.xsd, and file:///data/data/com.company.appname/files/xml.xsd, but all three resulted in the same libxml2 error:
domain: 16
code: 3069 (XML_SCHEMAP_INTERNAL)
message: Internal error: xmlSchemaParse, An internal error occurred.
I also tried removing the schemaLocation attribute entirely, on the off-chance that libxml2 might search for the imported schemas alongside the original schema, but that resulted in the following error when the schema parser hit a line that referenced the imported entities:
<xs:attribute ref="xml:lang" use="required"/>
domain: 16
code: 3004 (XML_SCHEMAP_SRC_RESOLVE)
message: attribute use (unknown), attribute 'ref': The QName value '{http://www.w3.org/XML/1998/namespace}lang' does not resolve to a(n) attribute declaration.
I also looked into manually merging the three schemas into a single file, but as they use different namespaces, this is not possible.
The standard solution for this seems to be the XML catalog, but I've read through libxml2's catalog documentation, and I can't figure out how (or even whether it's possible) to add mappings that will be used by my app when deployed to a device. I think I might need to implement an xmlExternalEntityLoader, but the documentation for that is quite slim.
How can I get libxml2 to import these schemas without network access? Obviously I'd ideally like a robust solution that works with the unedited schema, but I've be content with something quick-and-dirty that involves editing the schema, like my original attempts described above.
The errors described above are from an Android device (using JNI), but I'm having similar problems on iOS, where the solution will also need to work.
One way to do this is to intercept libxml2's call to open the imported URL with a custom xmlExternalEntityLoader.
The basic code for doing this is as follows:
#include <libxml/xmlIO.h>
#include <libxml/parserinternals.h>
xmlExternalEntityLoader defaultLoader = NULL;
xmlParserInputPtr
xmlMyExternalEntityLoader(const char *URL, const char *ID,
xmlParserCtxtPtr ctxt) {
xmlParserInputPtr ret;
const char *fileID = NULL;
/* lookup for the fileID
* The documentation suggests using the ID, but for me this was
* always NULL so I had to lookup by URL instead.
*/
ret = xmlNewInputFromFile(ctxt, fileID);
if (ret != NULL)
return(ret);
if (defaultLoader != NULL)
ret = defaultLoader(URL, ID, ctxt);
return(ret);
}
int main(..) {
...
/*
* Install our own entity loader
*/
defaultLoader = xmlGetExternalEntityLoader();
xmlSetExternalEntityLoader(xmlMyExternalEntityLoader);
...
}
(Slightly adjusted from the sample code in The entities loader section of libxml2's I/O Interfaces documentation.)

Categories

Resources