gradle-node-plugin icon indicating copy to clipboard operation
gradle-node-plugin copied to clipboard

PlatformHelper.getOsArch will be wrong when running 32-bit JVM on a 64-bit system

Open blongstreth opened this issue 7 years ago • 7 comments

The value of Java system property 'os.arch' will return the bitness of the JRE, not the Operating System itself. I think this may be an issue when trying to install the proper version of node, etc.

See the following links: http://mark.koli.ch/javas-osarch-system-property-is-the-bitness-of-the-jre-not-the-operating-system https://blogs.oracle.com/DieterDeramoudt/entry/beware_when_detecting_32_and

blongstreth avatar May 05 '17 23:05 blongstreth

Oh, that is bad. Any way of detecting this without using native calls?

srs avatar May 08 '17 09:05 srs

Well, I think the easiest solution is to follow Gradle. It appears they use the following library:

https://github.com/adammurdoch/native-platform

You will have to review the supported platforms to see if it is comprehensive enough. Meaning, I think it will work on any platform supported by Gradle itself which I believe is only a subset of what Node supports. Hopefully that is okay. If not, then Gradle is probably not the right tool. Anyway, Gradle internal code will execute the following command and do a fallback to the system 'os.arch' property if it fails (ironically):

// Attempt to get the native system architecture because "os.arch" system property
// is the architecture of the JRE, NOT the Operating System.  For example, it will report
// a 32-bit value when running a 32-bit JVM on a 64-bit OS.
try {
  def osArch = net.rubygrapefruit.platform.Native.get(SystemInfo.class).getArchitectureName()
} catch (Exception e) {
  //blah
}

Here is a link to the Gradle source code which fetches the current architecture (used internally): https://github.com/gradle/gradle/blob/f001402c8b7f404c923d972f1bc524b2de7b3fcd/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/platform/internal/DefaultNativePlatform.java See:

DefaultNativePlatform.getCurrentArchitecture()

Another thing I found useful is the following library since it seems to normalize the os name and architecture nicely: https://github.com/google/osdetector-gradle-plugin

I have not completed/finalized anything yet, but you will have to do something like the following code snippet in order to use the library with custom "os.arch" fetching (but not altering the system "os.arch" directly):

import kr.motd.maven.os.Detector
import net.rubygrapefruit.platform.SystemInfo
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging

class OsDetector extends Detector {
        private final Logger logger = Logging.getLogger(OsDetector)

        @Override
        protected void log(String message) {
            logger.info(message);
        }

        @Override
        protected void logProperty(String name, String value) {
            logger.info(name + "=" + value);
        }

        OsDetector(final Properties detectedProperties) {

            // Attempt to get the native system architecture because "os.arch" system property
            // is the architecture of the JRE, NOT the Operating System.  For example, it will report
            // a 32-bit value when running a 32-bit JVM on a 64-bit OS.
            try {
                def osArch = net.rubygrapefruit.platform.Native.get(SystemInfo.class).getArchitectureName()

                // overwrite 'os.arch'
                detectedProperties.put('os.arch', osArch)
            } catch (Exception e) {
                logger.error(e)
            }

            detect(detectedProperties, [])
        }
    }

Usage:

final OsDetector osDetector = new OsDetector(properties)
final String osName = osDetector.getProperty(OsDetector.DETECTED_NAME)
final String osArch = osDetector.getProperty(OsDetector.DETECTED_ARCH)

Finally,

It would be nice if Gradle did more to expose these things. Hopefully in the future they will.

Cheers, Bradley

blongstreth avatar May 09 '17 18:05 blongstreth

Or default the os.arch to 'x64' and not 'x86', since most users PC's or build systems are now 64-bit.

mdoornik avatar Oct 02 '17 10:10 mdoornik

Hit the same issue today. I agree the best thing is to default x64 as per @mdoornik comment above.

maiko-rocha avatar Nov 17 '17 01:11 maiko-rocha

I have managed to solve the issue myself but have no time to contribute exact code. I hope the following details make sense. If not, I will update this comment accordingly.

Regards,

Bradley

I use 2 libraries to help with managing the architecture:

  • os-maven-plugin
  • native-platform

Gradle Dependencies:

dependencies {
    compile gradleApi()
    compile localGroovy()
    compile 'kr.motd.maven:os-maven-plugin:1.5.0.Final'
    compile "net.rubygrapefruit:native-platform:0.10"
    testCompile group: 'junit', name: 'junit', version: '4.12'
    ...
}

Related code to finding proper architecture:

// Attempt to get the native system architecture because "os.type" system property
// is the architecture of the JRE, NOT the Operating System.  For example, it will report
// a 32-bit value when running a 32-bit JVM on a 64-bit OS.
try {
    final String osArch = Native.get(SystemInfo.class).getArchitectureName()
    // overwrite 'os.type'
   tempProps.put('os.arch', osArch)
} catch (final Exception e) {
    logger.error("Failed to fetch os.arch", e)
}

Where the System property 'os.arch' can be updated and any custom classes which could use the architecture name (like the maven-os-plugin classes).

Example class:

private static class Impl extends Detector {

        final Properties detectedProperties

        Impl(final Properties props) {

            final Properties tempProps = new Properties(props)

            // Attempt to get the native system architecture because "os.type" system property
            // is the architecture of the JRE, NOT the Operating System.  For example, it will report
            // a 32-bit value when running a 32-bit JVM on a 64-bit OS.
            try {
                final String osArch = Native.get(SystemInfo.class).getArchitectureName()

                // overwrite 'os.type'
                tempProps.put('os.arch', osArch)
            } catch (final Exception e) {
                logger.error("Failed to fetch os.arch", e)
            }
            detect(tempProps, [])

            this.detectedProperties = tempProps
        }

        @Override
        protected void log(final String message) {
            logger.info(message)
        }

        @Override
        protected void logProperty(final String name, final String value) {
            logger.info("{}={}", name, value)
        }
    }

blongstreth avatar Nov 22 '17 19:11 blongstreth

I met this problem too. System.getProperty('os.arch') does not return the proper arch number. How can I check the bitness of my OS using Java?? (J2SE, not os.arch) Any helps from this solution?

asahui avatar Apr 17 '18 06:04 asahui

I posted a working solution above using the net.rubygrapefruit:native-platform:0.10 java library, which ultimately uses the following method:

final String osArch = Native.get(SystemInfo.class).getArchitectureName()

blongstreth avatar Apr 17 '18 20:04 blongstreth