mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-11-26 06:11:19 +00:00
Improve building wrapper and native binaries on Windows and Linux
* Do not ask for root if root is disabled in settings * Show error in UI when libSyncthing.so is missing * build-syncthing - Install Go on demand on windows * build-syncthing - Install Android NDK on demand on windows * Update README.md * Update APK version to 0.14.51.rc3.6 / 4162
This commit is contained in:
parent
0203aebc40
commit
73775a116d
11 changed files with 377 additions and 199 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -32,6 +32,9 @@ gradle/wrapper/gradlew*
|
|||
# Prebuilt-go
|
||||
syncthing/go
|
||||
syncthing/go.tgz
|
||||
syncthing/go.zip
|
||||
syncthing/android-ndk-r16b
|
||||
syncthing/ndk.zip
|
||||
|
||||
# External build artifacts
|
||||
ext/
|
||||
|
|
30
README.md
30
README.md
|
@ -14,7 +14,7 @@
|
|||
|
||||
A wrapper of [Syncthing](https://github.com/syncthing/syncthing) for Android. Head to the "releases" section for builds. Please open an issue under this fork if you need help. Important: Please don't file bugs at the upstream repository "syncthing-android" if you are using this fork.
|
||||
|
||||
<img src="app/src/main/play/en-GB/listing/phoneScreenshots/screenshot_phone_1.png" alt="screenshot 1" width="200" /> <img src="app/src/main/play/en-GB/listing/phoneScreenshots/screenshot_phone_2.png" alt="screenshot 2" width="200" /> <img src="app/src/main/play/en-GB/listing/phoneScreenshots/screenshot_phone_3.png" alt="screenshot 3" width="200" />
|
||||
<img src="app/src/main/play/en-GB/listing/phoneScreenshots/screenshot_phone_12.png" alt="screenshot 1" width="200" /> <img src="app/src/main/play/en-GB/listing/phoneScreenshots/screenshot_phone_11.png" alt="screenshot 2" width="200" /> <img src="app/src/main/play/en-GB/listing/phoneScreenshots/screenshot_phone_09.png" alt="screenshot 3" width="200" />
|
||||
|
||||
# Translations
|
||||
|
||||
|
@ -22,21 +22,33 @@ The project is translated on [Transifex](https://www.transifex.com/projects/p/sy
|
|||
|
||||
# Building
|
||||
|
||||
### Dependencies
|
||||
- Android SDK (you can skip this if you are using Android Studio)
|
||||
- Android NDK (`$ANDROID_NDK_HOME` should point at the root directory of your NDK)
|
||||
- Go (see [here](https://docs.syncthing.net/dev/building.html#prerequisites) for the required version)
|
||||
### Prerequisites
|
||||
- Android SDK
|
||||
`You can skip this if you are using Android Studio.`
|
||||
- Android NDK r16b
|
||||
`$ANDROID_NDK_HOME environment variable should point at the root directory of your NDK. If the variable is not set, build-syncthing.py will automatically try to download and setup the NDK.`
|
||||
- Go 1.9.7
|
||||
`Make sure, Go is installed and available on the PATH environment variable. If Go is not found on the PATH environment variable, build-syncthing.py will automatically try to download and setup GO on the PATH.`
|
||||
- Python 2.7
|
||||
`Make sure, Python is installed and available on the PATH environment variable.`
|
||||
|
||||
### Build instructions
|
||||
|
||||
Make sure you clone the project with
|
||||
`git clone https://github.com/Catfriend1/syncthing-android.git --recursive`. Alternatively, run
|
||||
`git submodule init && git submodule update` in the project folder.
|
||||
`git clone https://github.com/Catfriend1/syncthing-android.git --recursive`.
|
||||
Alternatively, run `git submodule init && git submodule update` in the project folder.
|
||||
|
||||
A Linux VM, for example running Debian, is recommended to build this.
|
||||
|
||||
Build Syncthing using `./gradlew cleanNative buildNative`. Then use `./gradlew assembleDebug` or
|
||||
Android Studio to build the apk.
|
||||
Build Syncthing and the Syncthing-Android wrapper using the following commands:
|
||||
`./gradlew buildNative`
|
||||
`./gradlew lint assembleDebug`
|
||||
|
||||
You can also use Android Studio to build the apk after you manually ran the `./gradlew buildNative` command in the repository root.
|
||||
|
||||
To clean up all files generated during build, use the following commands:
|
||||
`./gradlew cleanNative`
|
||||
`./gradlew clean`
|
||||
|
||||
# License
|
||||
|
||||
|
|
|
@ -35,8 +35,8 @@ android {
|
|||
applicationId "com.github.catfriend1.syncthingandroid"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 26
|
||||
versionCode 4161
|
||||
versionName "0.14.51.rc3.5"
|
||||
versionCode 4162
|
||||
versionName "0.14.51.rc3.6"
|
||||
testApplicationId 'com.github.catfriend1.syncthingandroid.test'
|
||||
testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
|
||||
playAccountConfig = playAccountConfigs.defaultAccountConfig
|
||||
|
|
|
@ -39,9 +39,11 @@ import android.widget.Toast;
|
|||
import com.nutomic.syncthingandroid.R;
|
||||
import com.nutomic.syncthingandroid.SyncthingApp;
|
||||
import com.nutomic.syncthingandroid.service.Constants;
|
||||
import com.nutomic.syncthingandroid.service.SyncthingRunnable.ExecutableNotFoundException;
|
||||
import com.nutomic.syncthingandroid.util.ConfigXml;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class FirstStartActivity extends Activity {
|
||||
|
@ -79,7 +81,8 @@ public class FirstStartActivity extends Activity {
|
|||
private Button mBackButton;
|
||||
private Button mNextButton;
|
||||
|
||||
@Inject SharedPreferences mPreferences;
|
||||
@Inject
|
||||
SharedPreferences mPreferences;
|
||||
|
||||
/**
|
||||
* Handles activity behaviour depending on prerequisites.
|
||||
|
@ -479,6 +482,9 @@ public class FirstStartActivity extends Activity {
|
|||
}
|
||||
try {
|
||||
configXml = new ConfigXml(firstStartActivity);
|
||||
} catch (ExecutableNotFoundException e) {
|
||||
publishProgress(firstStartActivity.getString(R.string.executable_not_found, e.getMessage()));
|
||||
cancel(true);
|
||||
} catch (ConfigXml.OpenConfigException e) {
|
||||
publishProgress(firstStartActivity.getString(R.string.config_create_failed));
|
||||
cancel(true);
|
||||
|
|
|
@ -123,7 +123,6 @@ public class WebGuiActivity extends SyncthingActivity
|
|||
|
||||
/**
|
||||
* Initialize WebView.
|
||||
*
|
||||
* Ignore lint javascript warning as js is loaded only from our known, local service.
|
||||
*/
|
||||
@Override
|
||||
|
@ -134,7 +133,11 @@ public class WebGuiActivity extends SyncthingActivity
|
|||
setContentView(R.layout.activity_web_gui);
|
||||
|
||||
mLoadingView = findViewById(R.id.loading);
|
||||
try {
|
||||
mConfig = new ConfigXml(this);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
loadCaCert();
|
||||
|
||||
mWebView = findViewById(R.id.webview);
|
||||
|
|
|
@ -56,9 +56,13 @@ public class SyncthingRunnable implements Runnable {
|
|||
private final File mSyncthingBinary;
|
||||
private String[] mCommand;
|
||||
private final File mLogFile;
|
||||
@Inject SharedPreferences mPreferences;
|
||||
private final boolean mUseRoot;
|
||||
@Inject NotificationHandler mNotificationHandler;
|
||||
|
||||
@Inject
|
||||
SharedPreferences mPreferences;
|
||||
|
||||
@Inject
|
||||
NotificationHandler mNotificationHandler;
|
||||
|
||||
public enum Command {
|
||||
deviceid, // Output the device ID to the command line.
|
||||
|
@ -104,11 +108,15 @@ public class SyncthingRunnable implements Runnable {
|
|||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
run(false);
|
||||
} catch (ExecutableNotFoundException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("WakelockTimeout")
|
||||
public String run(boolean returnStdOut) {
|
||||
public String run(boolean returnStdOut) throws ExecutableNotFoundException {
|
||||
Boolean sendStopToService = false;
|
||||
Boolean restartSyncthingNative = false;
|
||||
int exitCode;
|
||||
|
@ -315,7 +323,11 @@ public class SyncthingRunnable implements Runnable {
|
|||
* Manually run "sysctl fs.inotify" in a root shell terminal to check current limit.
|
||||
*/
|
||||
private void increaseInotifyWatches() {
|
||||
if (!mUseRoot || !Shell.SU.available()) {
|
||||
if (!mUseRoot) {
|
||||
// Settings prohibit using root privileges. Cannot increase inotify limit.
|
||||
return;
|
||||
}
|
||||
if (!Shell.SU.available()) {
|
||||
Log.i(TAG, "increaseInotifyWatches: Root is not available. Cannot increase inotify limit.");
|
||||
return;
|
||||
}
|
||||
|
@ -327,7 +339,11 @@ public class SyncthingRunnable implements Runnable {
|
|||
* Look for a running libsyncthing.so process and nice its IO.
|
||||
*/
|
||||
private void niceSyncthing() {
|
||||
if (!mUseRoot || !Shell.SU.available()) {
|
||||
if (!mUseRoot) {
|
||||
// Settings prohibit using root privileges. Cannot nice syncthing.
|
||||
return;
|
||||
}
|
||||
if (!Shell.SU.available()) {
|
||||
Log.i(TAG_NICE, "Root is not available. Cannot nice syncthing.");
|
||||
return;
|
||||
}
|
||||
|
@ -482,7 +498,16 @@ public class SyncthingRunnable implements Runnable {
|
|||
return targetEnv;
|
||||
}
|
||||
|
||||
private Process setupAndLaunch(HashMap<String, String> env) throws IOException {
|
||||
private Process setupAndLaunch(HashMap<String, String> env) throws IOException, ExecutableNotFoundException {
|
||||
// Check if "libSyncthing.so" exists.
|
||||
if (mCommand.length > 0) {
|
||||
File libSyncthing = new File(mCommand[0]);
|
||||
if (!libSyncthing.exists()) {
|
||||
Log.e(TAG, "CRITICAL - Syncthing core binary is missing in APK package location " + mCommand[0]);
|
||||
throw new ExecutableNotFoundException(mCommand[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (mUseRoot) {
|
||||
ProcessBuilder pb = new ProcessBuilder("su");
|
||||
Process process = pb.start();
|
||||
|
@ -508,4 +533,16 @@ public class SyncthingRunnable implements Runnable {
|
|||
return pb.start();
|
||||
}
|
||||
}
|
||||
|
||||
public class ExecutableNotFoundException extends Exception {
|
||||
|
||||
public ExecutableNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ExecutableNotFoundException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,18 +120,27 @@ public class SyncthingService extends Service {
|
|||
* Indicates the current state of SyncthingService and of Syncthing itself.
|
||||
*/
|
||||
public enum State {
|
||||
/** Service is initializing, Syncthing was not started yet. */
|
||||
/**
|
||||
* Service is initializing, Syncthing was not started yet.
|
||||
*/
|
||||
INIT,
|
||||
/** Syncthing binary is starting. */
|
||||
/**
|
||||
* Syncthing binary is starting.
|
||||
*/
|
||||
STARTING,
|
||||
/** Syncthing binary is running,
|
||||
/**
|
||||
* Syncthing binary is running,
|
||||
* Rest API is available,
|
||||
* RestApi class read the config and is fully initialized.
|
||||
*/
|
||||
ACTIVE,
|
||||
/** Syncthing binary is shutting down. */
|
||||
/**
|
||||
* Syncthing binary is shutting down.
|
||||
*/
|
||||
DISABLED,
|
||||
/** There is some problem that prevents Syncthing from running. */
|
||||
/**
|
||||
* There is some problem that prevents Syncthing from running.
|
||||
*/
|
||||
ERROR,
|
||||
}
|
||||
|
||||
|
@ -141,13 +150,7 @@ public class SyncthingService extends Service {
|
|||
* {@link onStartCommand}.
|
||||
*/
|
||||
private State mCurrentState = State.DISABLED;
|
||||
|
||||
private ConfigXml mConfig;
|
||||
private @Nullable PollWebGuiAvailableTask mPollWebGuiAvailableTask = null;
|
||||
private @Nullable RestApi mApi = null;
|
||||
private @Nullable EventProcessor mEventProcessor = null;
|
||||
private @Nullable RunConditionMonitor mRunConditionMonitor = null;
|
||||
private @Nullable SyncthingRunnable mSyncthingRunnable = null;
|
||||
private StartupTask mStartupTask = null;
|
||||
private Thread mSyncthingRunnableThread = null;
|
||||
private Handler mHandler;
|
||||
|
@ -155,8 +158,26 @@ public class SyncthingService extends Service {
|
|||
private final HashSet<OnServiceStateChangeListener> mOnServiceStateChangeListeners = new HashSet<>();
|
||||
private final SyncthingServiceBinder mBinder = new SyncthingServiceBinder(this);
|
||||
|
||||
@Inject NotificationHandler mNotificationHandler;
|
||||
@Inject SharedPreferences mPreferences;
|
||||
private @Nullable
|
||||
PollWebGuiAvailableTask mPollWebGuiAvailableTask = null;
|
||||
|
||||
private @Nullable
|
||||
RestApi mApi = null;
|
||||
|
||||
private @Nullable
|
||||
EventProcessor mEventProcessor = null;
|
||||
|
||||
private @Nullable
|
||||
RunConditionMonitor mRunConditionMonitor = null;
|
||||
|
||||
private @Nullable
|
||||
SyncthingRunnable mSyncthingRunnable = null;
|
||||
|
||||
@Inject
|
||||
NotificationHandler mNotificationHandler;
|
||||
|
||||
@Inject
|
||||
SharedPreferences mPreferences;
|
||||
|
||||
/**
|
||||
* Object that must be locked upon accessing mCurrentState
|
||||
|
@ -245,7 +266,8 @@ public class SyncthingService extends Service {
|
|||
if (ACTION_RESTART.equals(intent.getAction()) && mCurrentState == State.ACTIVE) {
|
||||
shutdown(State.INIT, () -> launchStartupTask(SyncthingRunnable.Command.main));
|
||||
} else if (ACTION_STOP.equals(intent.getAction()) && mCurrentState == State.ACTIVE) {
|
||||
shutdown(State.DISABLED, () -> {});
|
||||
shutdown(State.DISABLED, () -> {
|
||||
});
|
||||
} else if (ACTION_RESET_DATABASE.equals(intent.getAction())) {
|
||||
Log.i(TAG, "Invoking reset of database");
|
||||
shutdown(State.INIT, () -> {
|
||||
|
@ -305,7 +327,8 @@ public class SyncthingService extends Service {
|
|||
return;
|
||||
}
|
||||
Log.v(TAG, "Stopping syncthing");
|
||||
shutdown(State.DISABLED, () -> {});
|
||||
shutdown(State.DISABLED, () -> {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -355,6 +378,12 @@ public class SyncthingService extends Service {
|
|||
try {
|
||||
syncthingService.mConfig = new ConfigXml(syncthingService);
|
||||
syncthingService.mConfig.updateIfNeeded();
|
||||
} catch (SyncthingRunnable.ExecutableNotFoundException e) {
|
||||
syncthingService.mNotificationHandler.showCrashedNotification(R.string.config_read_failed, "SycnthingRunnable.ExecutableNotFoundException");
|
||||
synchronized (syncthingService.mStateLock) {
|
||||
syncthingService.onServiceStateChange(State.ERROR);
|
||||
}
|
||||
cancel(true);
|
||||
} catch (ConfigXml.OpenConfigException e) {
|
||||
syncthingService.mNotificationHandler.showCrashedNotification(R.string.config_read_failed, "ConfigXml.OpenConfigException");
|
||||
synchronized (syncthingService.mStateLock) {
|
||||
|
@ -483,21 +512,22 @@ public class SyncthingService extends Service {
|
|||
mDestroyScheduled = true;
|
||||
} else {
|
||||
Log.i(TAG, "Shutting down syncthing binary immediately");
|
||||
shutdown(State.DISABLED, () -> {});
|
||||
shutdown(State.DISABLED, () -> {
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the storage permission got revoked, we did not start the binary and
|
||||
// are in State.INIT requiring an immediate shutdown of this service class.
|
||||
Log.i(TAG, "Shutting down syncthing binary due to missing storage permission.");
|
||||
shutdown(State.DISABLED, () -> {});
|
||||
shutdown(State.DISABLED, () -> {
|
||||
});
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop Syncthing and all helpers like event processor and api handler.
|
||||
*
|
||||
* Sets {@link #mCurrentState} to newState, and calls onKilledListener once Syncthing is killed.
|
||||
*/
|
||||
private void shutdown(State newState, OnSyncthingKilled onKilledListener) {
|
||||
|
@ -550,7 +580,8 @@ public class SyncthingService extends Service {
|
|||
onKilledListener.onKilled();
|
||||
}
|
||||
|
||||
public @Nullable RestApi getApi() {
|
||||
public @Nullable
|
||||
RestApi getApi() {
|
||||
return mApi;
|
||||
}
|
||||
|
||||
|
@ -568,7 +599,6 @@ public class SyncthingService extends Service {
|
|||
|
||||
/**
|
||||
* Register a listener for the syncthing API state changing.
|
||||
*
|
||||
* The listener is called immediately with the current state, and again whenever the state
|
||||
* changes. The call is always from the GUI thread.
|
||||
*
|
||||
|
@ -692,7 +722,8 @@ public class SyncthingService extends Service {
|
|||
Log.v(TAG, "importConfig BEGIN");
|
||||
|
||||
// Shutdown synchronously.
|
||||
shutdown(State.DISABLED, () -> {});
|
||||
shutdown(State.DISABLED, () -> {
|
||||
});
|
||||
|
||||
// Import config, privateKey and/or publicKey.
|
||||
try {
|
||||
|
@ -736,11 +767,13 @@ public class SyncthingService extends Service {
|
|||
// Preferences that are no longer used and left-overs from previous versions of the app.
|
||||
case "first_start":
|
||||
case "notify_crashes":
|
||||
Log.v(TAG, "importConfig: Ignoring deprecated pref \"" + prefKey + "\".");
|
||||
break;
|
||||
// Cached information which is not available on SettingsActivity.
|
||||
case Constants.PREF_DEBUG_FACILITIES_AVAILABLE:
|
||||
case Constants.PREF_EVENT_PROCESSOR_LAST_SYNC_ID:
|
||||
case Constants.PREF_LAST_BINARY_VERSION:
|
||||
Log.v(TAG, "importConfig: Ignoring pref \"" + prefKey + "\".");
|
||||
Log.v(TAG, "importConfig: Ignoring cache pref \"" + prefKey + "\".");
|
||||
break;
|
||||
default:
|
||||
Log.v(TAG, "importConfig: Adding pref \"" + prefKey + "\" to commit ...");
|
||||
|
|
|
@ -41,7 +41,6 @@ import javax.xml.transform.stream.StreamResult;
|
|||
|
||||
/**
|
||||
* Provides direct access to the config.xml file in the file system.
|
||||
*
|
||||
* This class should only be used if the syncthing API is not available (usually during startup).
|
||||
*/
|
||||
public class ConfigXml {
|
||||
|
@ -53,19 +52,21 @@ public class ConfigXml {
|
|||
private static final int FOLDER_ID_APPENDIX_LENGTH = 4;
|
||||
|
||||
private final Context mContext;
|
||||
@Inject SharedPreferences mPreferences;
|
||||
|
||||
@Inject
|
||||
SharedPreferences mPreferences;
|
||||
|
||||
private final File mConfigFile;
|
||||
|
||||
private Document mConfig;
|
||||
|
||||
public ConfigXml(Context context) throws OpenConfigException {
|
||||
public ConfigXml(Context context) throws OpenConfigException, SyncthingRunnable.ExecutableNotFoundException {
|
||||
mContext = context;
|
||||
mConfigFile = Constants.getConfigFile(mContext);
|
||||
boolean isFirstStart = !mConfigFile.exists();
|
||||
if (isFirstStart) {
|
||||
Log.i(TAG, "App started for the first time. Generating keys and config.");
|
||||
new SyncthingRunnable(context, SyncthingRunnable.Command.generate).run();
|
||||
new SyncthingRunnable(context, SyncthingRunnable.Command.generate).run(true);
|
||||
}
|
||||
|
||||
readConfig();
|
||||
|
@ -122,7 +123,6 @@ public class ConfigXml {
|
|||
|
||||
/**
|
||||
* Updates the config file.
|
||||
*
|
||||
* Sets ignorePerms flag to true on every folder, force enables TLS, sets the
|
||||
* username/password, and disables weak hash checking.
|
||||
*/
|
||||
|
@ -210,7 +210,6 @@ public class ConfigXml {
|
|||
|
||||
/**
|
||||
* Updates syncthing options to a version specific target setting in the config file.
|
||||
*
|
||||
* Used for one-time config migration from a lower syncthing version to the current version.
|
||||
* Enables filesystem watcher.
|
||||
* Returns if changes to the config have been made.
|
||||
|
@ -273,7 +272,6 @@ public class ConfigXml {
|
|||
|
||||
/**
|
||||
* Set device model name as device name for Syncthing.
|
||||
*
|
||||
* We need to iterate through XML nodes manually, as mConfig.getDocumentElement() will also
|
||||
* return nested elements inside folder element. We have to check that we only rename the
|
||||
* device corresponding to the local device ID.
|
||||
|
|
|
@ -652,6 +652,7 @@ Please report any problems you encounter via Github.</string>
|
|||
<string name="syncthing_terminated">Syncthing was terminated</string>
|
||||
|
||||
<!-- Toast shown if syncthing failed to create or read the config -->
|
||||
<string name="executable_not_found">Core executable \"%s\" is missing. Check build and logcat output.</string>
|
||||
<string name="config_create_failed">Failed to create configuration. Check logcat output.</string>
|
||||
<string name="config_read_failed">Failed to read configuration. Consider backing up data from your sync folders, then clear this app\'s data from Android settings and launch it again.</string>
|
||||
|
||||
|
|
|
@ -44,17 +44,13 @@ def get_min_sdk(project_dir):
|
|||
|
||||
fail('Failed to find minSdkVersion')
|
||||
|
||||
|
||||
def get_ndk_home():
|
||||
if not os.environ.get('ANDROID_NDK_HOME', ''):
|
||||
fail('Error: ANDROID_NDK_HOME environment variable not defined')
|
||||
return os.environ['ANDROID_NDK_HOME']
|
||||
|
||||
def which(program):
|
||||
import os
|
||||
def is_exe(fpath):
|
||||
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
|
||||
|
||||
if (sys.platform == 'win32'):
|
||||
program += ".exe"
|
||||
fpath, fname = os.path.split(program)
|
||||
if fpath:
|
||||
if is_exe(program):
|
||||
|
@ -67,26 +63,40 @@ def which(program):
|
|||
|
||||
return None
|
||||
|
||||
def change_permissions_recursive(path, mode):
|
||||
import os
|
||||
for root, dirs, files in os.walk(path, topdown=False):
|
||||
for dir in [os.path.join(root,d) for d in dirs]:
|
||||
os.chmod(dir, mode)
|
||||
for file in [os.path.join(root, f) for f in files]:
|
||||
os.chmod(file, mode)
|
||||
|
||||
def install_go():
|
||||
import os
|
||||
import tarfile
|
||||
import zipfile
|
||||
import urllib
|
||||
import hashlib
|
||||
|
||||
# Consts.
|
||||
pwd_path = os.path.dirname(os.path.realpath(__file__))
|
||||
if sys.platform == 'win32':
|
||||
url = 'https://dl.google.com/go/go1.9.7.windows-amd64.zip'
|
||||
expected_shasum = '8db4b21916a3bc79f48d0611202ee5814c82f671b36d5d2efcb446879456cd28'
|
||||
tar_gz_fullfn = pwd_path + os.path.sep + 'go.zip';
|
||||
else:
|
||||
url = 'https://dl.google.com/go/go1.9.7.linux-amd64.tar.gz'
|
||||
expected_shasum = '88573008f4f6233b81f81d8ccf92234b4f67238df0f0ab173d75a302a1f3d6ee'
|
||||
tar_gz_fullfn = pwd_path + os.path.sep + 'go.tgz';
|
||||
|
||||
# Download prebuilt-go.
|
||||
url_base_name = os.path.basename(url)
|
||||
tar_gz_fullfn = pwd_path + os.path.sep + 'go.tgz';
|
||||
if not os.path.isfile(tar_gz_fullfn):
|
||||
print('Downloading prebuilt-go tar to:', tar_gz_fullfn)
|
||||
print('Downloading prebuilt-go to:', tar_gz_fullfn)
|
||||
tar_gz_fullfn = urllib.urlretrieve(url, tar_gz_fullfn)[0]
|
||||
print('Downloaded prebuilt-go tar to:', tar_gz_fullfn)
|
||||
print('Downloaded prebuilt-go to:', tar_gz_fullfn)
|
||||
|
||||
# Verfiy SHA-1 checksum of downloaded files.
|
||||
# Verfiy SHA-256 checksum of downloaded files.
|
||||
with open(tar_gz_fullfn, 'rb') as f:
|
||||
contents = f.read()
|
||||
found_shasum = hashlib.sha256(contents).hexdigest()
|
||||
|
@ -96,11 +106,18 @@ def install_go():
|
|||
print("[ok] Checksum of", tar_gz_fullfn, "matches expected value.")
|
||||
|
||||
# Proceed with extraction of the prebuilt go.
|
||||
# This will go to a subfolder "go" in the current path.
|
||||
if not os.path.isfile(pwd_path + os.path.sep + 'go' + os.path.sep + 'LICENSE'):
|
||||
print("Extracting prebuilt-go ...")
|
||||
# This will go to a subfolder "go" in the current path.
|
||||
file_name, file_extension = os.path.splitext(url_base_name)
|
||||
if sys.platform == 'win32':
|
||||
zip = zipfile.ZipFile(tar_gz_fullfn, 'r')
|
||||
zip.extractall(pwd_path)
|
||||
zip.close()
|
||||
else:
|
||||
tar = tarfile.open(tar_gz_fullfn)
|
||||
tar.extractall(pwd_path)
|
||||
tar.close()
|
||||
|
||||
# Add (...).tar/go/bin" to the PATH.
|
||||
go_bin_path = pwd_path + os.path.sep + 'go' + os.path.sep + 'bin'
|
||||
|
@ -108,6 +125,65 @@ def install_go():
|
|||
os.environ["PATH"] += os.pathsep + go_bin_path
|
||||
|
||||
|
||||
|
||||
|
||||
def install_ndk():
|
||||
import os
|
||||
import zipfile
|
||||
import urllib
|
||||
import hashlib
|
||||
|
||||
# Consts.
|
||||
pwd_path = os.path.dirname(os.path.realpath(__file__))
|
||||
if sys.platform == 'win32':
|
||||
url = 'https://dl.google.com/android/repository/android-ndk-r16b-windows-x86_64.zip'
|
||||
expected_shasum = 'f3f1909ed1052e98dda2c79d11c22f3da28daf25'
|
||||
|
||||
else:
|
||||
url = 'https://dl.google.com/android/repository/android-ndk-r16b-linux-x86_64.zip'
|
||||
expected_shasum = '42aa43aae89a50d1c66c3f9fdecd676936da6128'
|
||||
|
||||
zip_fullfn = pwd_path + os.path.sep + 'ndk.zip';
|
||||
# Download NDK.
|
||||
url_base_name = os.path.basename(url)
|
||||
if not os.path.isfile(zip_fullfn):
|
||||
print('Downloading NDK to:', zip_fullfn)
|
||||
zip_fullfn = urllib.urlretrieve(url, zip_fullfn)[0]
|
||||
print('Downloaded NDK to:', zip_fullfn)
|
||||
|
||||
# Verfiy SHA-1 checksum of downloaded files.
|
||||
with open(zip_fullfn, 'rb') as f:
|
||||
contents = f.read()
|
||||
found_shasum = hashlib.sha1(contents).hexdigest()
|
||||
print("SHA-1:", zip_fullfn, "%s" % found_shasum)
|
||||
if found_shasum != expected_shasum:
|
||||
fail('Error: SHA-1 checksum', found_shasum, 'of downloaded file does not match expected checksum', expected_shasum)
|
||||
print("[ok] Checksum of", zip_fullfn, "matches expected value.")
|
||||
|
||||
# Proceed with extraction of the NDK if necessary.
|
||||
ndk_home_path = pwd_path + os.path.sep + 'android-ndk-r16b'
|
||||
if not os.path.isfile(ndk_home_path + os.path.sep + "sysroot" + os.path.sep + "NOTICE"):
|
||||
print("Extracting NDK ...")
|
||||
# This will go to a subfolder "android-ndk-r16b" in the current path.
|
||||
file_name, file_extension = os.path.splitext(url_base_name)
|
||||
zip = zipfile.ZipFile(zip_fullfn, 'r')
|
||||
zip.extractall(pwd_path)
|
||||
zip.close()
|
||||
|
||||
# Linux only - Set executable permission on files.
|
||||
if platform.system() == 'Linux':
|
||||
print("Setting permissions on NDK executables ...")
|
||||
change_permissions_recursive(ndk_home_path, 0o755);
|
||||
|
||||
# Add "ANDROID_NDK_HOME" environment variable.
|
||||
print('Adding ANDROID_NDK_HOME=\'' + ndk_home_path + '\'')
|
||||
os.environ["ANDROID_NDK_HOME"] = ndk_home_path
|
||||
|
||||
|
||||
|
||||
#
|
||||
# BUILD SCRIPT MAIN.
|
||||
#
|
||||
if platform.system() not in SUPPORTED_PYTHON_PLATFORMS:
|
||||
fail('Unsupported python platform %s. Supported platforms: %s', platform.system(),
|
||||
', '.join(SUPPORTED_PYTHON_PLATFORMS))
|
||||
|
@ -125,12 +201,20 @@ go_bin = which("go");
|
|||
if not go_bin:
|
||||
print('Warning: go is not available on the PATH.')
|
||||
install_go();
|
||||
|
||||
# Retry: Check if go is available.
|
||||
go_bin = which("go");
|
||||
if not go_bin:
|
||||
fail('Error: go is not available on the PATH.')
|
||||
print ('go_bin [', go_bin, ']')
|
||||
print ('go_bin=\'' + go_bin + '\'')
|
||||
|
||||
# Check if ANDROID_NDK_HOME variable is set.
|
||||
if not os.environ.get('ANDROID_NDK_HOME', ''):
|
||||
print('Warning: ANDROID_NDK_HOME environment variable not defined.')
|
||||
install_ndk();
|
||||
# Retry: Check if ANDROID_NDK_HOME variable is set.
|
||||
if not os.environ.get('ANDROID_NDK_HOME', ''):
|
||||
fail('Error: ANDROID_NDK_HOME environment variable not defined')
|
||||
print ('ANDROID_NDK_HOME=\'' + os.environ.get('ANDROID_NDK_HOME', '') + '\'')
|
||||
|
||||
# Make sure all tags are available for git describe
|
||||
# https://github.com/syncthing/syncthing-android/issues/872
|
||||
|
@ -148,7 +232,7 @@ for target in BUILD_TARGETS:
|
|||
|
||||
if os.environ.get('SYNCTHING_ANDROID_PREBUILT', ''):
|
||||
# The environment variable indicates the SDK and stdlib was prebuilt, set a custom paths.
|
||||
standalone_ndk_dir = get_ndk_home() + os.path.sep + 'standalone-ndk' + os.path.sep + 'android-' + target_min_sdk + '-' + target['goarch']
|
||||
standalone_ndk_dir = os.environ['ANDROID_NDK_HOME'] + os.path.sep + 'standalone-ndk' + os.path.sep + 'android-' + target_min_sdk + '-' + target['goarch']
|
||||
pkg_argument = []
|
||||
else:
|
||||
# Build standalone NDK toolchain if it doesn't exist.
|
||||
|
@ -160,7 +244,7 @@ for target in BUILD_TARGETS:
|
|||
print('Building standalone NDK for', target['arch'], 'API level', target_min_sdk, 'to', standalone_ndk_dir)
|
||||
subprocess.check_call([
|
||||
sys.executable,
|
||||
os.path.join(get_ndk_home(), 'build', 'tools', 'make_standalone_toolchain.py'),
|
||||
os.path.join(os.environ['ANDROID_NDK_HOME'], 'build', 'tools', 'make_standalone_toolchain.py'),
|
||||
'--arch',
|
||||
target['arch'],
|
||||
'--api',
|
||||
|
|
|
@ -10,6 +10,7 @@ task buildNative(type: Exec) {
|
|||
*/
|
||||
task cleanNative(type: Delete) {
|
||||
delete "$projectDir/../app/src/main/jniLibs/"
|
||||
delete "android-ndk-r16b"
|
||||
delete "gobuild"
|
||||
delete "go"
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue