web-dev-qa-db-fra.com

Quelle version de javac a construit mon pot?

Comment savoir quelle version du compilateur Java a été utilisée pour créer un fichier jar? J'ai un fichier jar, et il aurait pu être construit dans l'un des trois JDK. Nous devons savoir exactement lequel pour pouvoir certifier la compatibilité. La version du compilateur est-elle intégrée quelque part dans les fichiers de classe ou le fichier jar?

195
Ron Romero

Vous ne pouvez pas nécessairement savoir à partir du fichier JAR lui-même.

Téléchargez un éditeur hexadécimal, ouvrez l'un des fichiers de classe à l'intérieur du fichier JAR et examinez les décalages d'octet 4 à 7. Les informations de version sont intégrées.

http://en.wikipedia.org/wiki/Java_class_file

Remarque: comme mentionné dans le commentaire ci-dessous,

ces octets vous indiquent quelle version la classe a été compilée POUR, pas quelle version l'a compilée.

85
Jonathon Faust

jar est simplement un conteneur. C'est une archive de fichier à la tar . Bien que jar puisse contenir des informations intéressantes dans sa hiérarchie META-INF , il n’est pas obligatoire de spécifier l’époque des classes dans son contenu. Pour cela, il faut examiner les fichiers class.

Comme Peter Lawrey mentionné dans le commentaire de la question initiale, vous ne pouvez pas nécessairement savoir quelle version du JDK a construit un fichier class donné, mais vous pouvez trouver la version de classe de code en octets du fichier class contenue dans jar.

Oui, c'est un peu nul, mais la première étape consiste à extraire une ou plusieurs classes de jar. Par exemple:

$ jar xf log4j-1.2.15.jar

Sous Linux, Mac OS X ou Windows avec Cygwin installé, la commande fichier (1) connaît la version de la classe.

$ file ./org/Apache/log4j/Appender.class
./org/Apache/log4j/Appender.class: compiled Java class data, version 45.3

Ou bien, utiliser javap à partir du JDK comme @ jikes.Thunderbolt fait remarquer:

$ javap -v ./org/Apache/log4j/Appender.class | grep major
 major version: 45

Et si vous êtes relégué dans un environnement Windows sans file ni grep

> javap -v ./org/Apache/log4j/Appender.class | findstr major
 major version: 45

FWIW, je conviens que javap en dira beaucoup plus sur un fichier class donné que la question initiale posée.

Quoi qu'il en soit, une version de classe différente, par exemple:

$ file ~/bin/classes/P.class
/home/dave/bin/classes/P.class: compiled Java class data, version 50.0

Le numéro majeur de version de classe correspond aux versions Java JDK suivantes:

  • 45.3 = Java 1.1
  • 46 = Java 1.2
  • 47 = Java 1.3
  • 48 = Java 1.4
  • 49 = Java 5
  • 50 = Java 6
  • 51 = Java 7
  • 52 = Java 8
  • 53 = Java 9
298
David J. Liszewski

Voici comment Java trouve ces informations.

Windows: javap -v <class> | findstr major
Unix: javap -v <class> | grep major

Par exemple:
> javap -v Application | findstr major   major version: 51

51
jikes.thunderbolt

Le compilateur Java (javac) ne construit pas de fichiers jar, il traduit les fichiers Java en fichiers de classe. L'outil Jar (jar) crée les pots réels. Si aucun manifeste personnalisé n'a été spécifié, le manifeste par défaut précisera quelle version du JDK a été utilisée pour créer le fichier JAR.

12
jackrabbit

Il n’est pas nécessaire de décompresser le fichier JAR (si l’un des noms de classe est connu ou recherché, par exemple à l’aide de 7Zip), les éléments suivants sont suffisants sous Windows:

javap -cp log4j-core-2.5.jar -verbose org.Apache.logging.log4j.core.Logger | findstr major
11
anre

Comme j'avais besoin d'analyser de gros pots, je m'intéressais à la version de chaque classe dans un fichier jar. Par conséquent, j’ai adopté l’approche de Joe Liversedge https://stackoverflow.com/a/27877215/1497139 et l’a combinée avec David J. Liszewski ' https://stackoverflow.com/a/3313839/1497139 table de version de numéro de classe pour créer un script jarv de bash afin d'afficher les versions de tous les fichiers de classe d'un fichier jar.

usage

usage: ./jarv jarfile
 -h|--help: show this usage

Exemple

jarv $Home/.m2/repository/log4j/log4j/1.2.17/log4j-1.2.17.jar

Java 1.4 org.Apache.log4j.Appender
Java 1.4 org.Apache.log4j.AppenderSkeleton
Java 1.4 org.Apache.log4j.AsyncAppender$DiscardSummary
Java 1.4 org.Apache.log4j.AsyncAppender$Dispatcher
...

Bash script jarv

#!/bin/bash
# WF 2018-07-12
# find out the class versions with in jar file
# see https://stackoverflow.com/questions/3313532/what-version-of-javac-built-my-jar

# uncomment do debug
# set -x

#ansi colors
#http://www.csc.uvic.ca/~sae/seng265/fall04/tips/s265s047-tips/bash-using-colors.html
blue='\033[0;34m'  
red='\033[0;31m'  
green='\033[0;32m' # '\e[1;32m' is too bright for white bg.
endColor='\033[0m'

#
# a colored message 
#   params:
#     1: l_color - the color of the message
#     2: l_msg - the message to display
#
color_msg() {
  local l_color="$1"
  local l_msg="$2"
  echo -e "${l_color}$l_msg${endColor}"
}

#
# error
#
#   show an error message and exit
#
#   params:
#     1: l_msg - the message to display
error() {
  local l_msg="$1"
  # use ansi red for error
  color_msg $red "Error: $l_msg" 1>&2
  exit 1
}

#
# show the usage
#
usage() {
  echo "usage: $0 jarfile"
  # -h|--help|usage|show this usage
  echo " -h|--help: show this usage"
  exit 1 
}

#
# showclassversions
#
showclassversions() {
  local l_jar="$1"
  jar -tf "$l_jar" | grep '.class' | while read classname
  do
    class=$(echo $classname | sed -e 's/\.class$//')
    class_version=$(javap -classpath "$l_jar" -verbose $class | grep 'major version' | cut -f2 -d ":" | cut -c2-)
    class_pretty=$(echo $class | sed -e 's#/#.#g')
    case $class_version in
      45.3) Java_version="Java 1.1";;
      46) Java_version="Java 1.2";;
      47) Java_version="Java 1.3";;
      48) Java_version="Java 1.4";;
      49) Java_version="Java5";;
      50) Java_version="Java6";;
      51) Java_version="Java7";;
      52) Java_version="Java8";;
      53) Java_version="Java9";;
      54) Java_version="Java10";;
      *) Java_version="x${class_version}x";;
    esac
    echo $Java_version $class_pretty
  done
}

# check the number of parameters
if [ $# -lt 1 ]
then
  usage
fi

# start of script
# check arguments
while test $# -gt 0
do
  case $1 in
    # -h|--help|usage|show this usage
    -h|--help) 
      usage
      exit 1
      ;;
    *)
     showclassversions "$1"
  esac
  shift
done 
7
Wolfgang Fahl

vous pouvez trouver la version du compilateur Java à partir de fichiers .class à l'aide d'un éditeur hexadécimal.

Étape 1: Extraire les fichiers .class du fichier jar à l'aide d'un extracteur Zip

étape 2: ouvrez le fichier .class avec un éditeur hexadécimal (j'ai utilisé le plugin notepad ++ hex editor. Ce plugin lit le fichier en tant que binaire et l'affiche en hexadécimal). enter image description here

Les index 6 et 7 donnent le numéro de version majeur du format de fichier de classe utilisé. https://en.wikipedia.org/wiki/Java_class_file

Java SE 11 = 55 (0x37 hex)

Java SE 10 = 54 (0x36 hex)

Java SE 9 = 53 (0x35 hex)

Java SE 8 = 52 (0x34 hex),

Java SE 7 = 51 (0x33 hex),

Java SE 6.0 = 50 (0x32 hex),

Java SE 5.0 = 49 (0x31 hex),

JDK 1,4 = 48 (0x30 hex),

JDK 1,3 = 47 (0x2F hex),

JDK 1.2 = 46 (0x2E hex),

JDK 1.1 = 45 (0x2D hex).

6
cahit beyaz

Le code posté par Owen peut vous dire les informations mentionnées par un certain nombre d'autres réponses ici:

public void simpleExample ()
{
    FileInputStream fis = new FileInputStream ("mytest.class");
    parseJavaClassFile ( fis );
}
protected void parseJavaClassFile ( InputStream classByteStream ) throws Exception
{
    DataInputStream dataInputStream = new DataInputStream ( classByteStream );
    magicNumber = dataInputStream.readInt();
    if ( magicNumber == 0xCAFEBABE )
    {
        int minorVer = dataInputStream.readUnsignedShort();
        int majorVer = dataInputStream.readUnsignedShort();
        // do something here with major & minor numbers
    }
}

Voir aussi this et this site. J'ai fini par modifier rapidement le code de Mind Products pour vérifier le but de chacune de mes dépendances.

4
JasonPlutext

Vous pouvez dire à la version binaire Java par inspectant les 8 premiers octets (ou en utilisant un app qui peut).

À ma connaissance, le compilateur lui-même n'insère aucune signature d'identification. Je ne peux pas repérer une telle chose dans le fichier format de classe de spécification VM de toute façon.

4
McDowell

Une doublure (Linux)

unzip -p mylib.jar META-INF/MANIFEST.MF

Ceci affiche le contenu du fichier MANIFEST.MF sur la sortie standard (espérons-le, il y en a un dans votre fichier jar :)

En fonction de la construction de votre package, vous trouverez la version du JDK dans la touche Created-By ou Build-Jdk.

3
Neal Xiong

Les développeurs et les administrateurs qui exécutent Bash peuvent trouver ces fonctions pratiques utiles:

jar_jdk_version() {
  [[ -n "$1" && -x "`command -v javap`" ]] && javap -classpath "$1" -verbose $(jar -tf "$1" | grep '.class' | head -n1 | sed -e 's/\.class$//') | grep 'major version' | sed -e 's/[^0-9]\{1,\}//'
}

print_jar_jdk_version() {
  local version
  version=$(jar_jdk_version "$1")
  case $version in 49) version=1.5;; 50) version=1.6;; 51) version=1.7;; 52) version=1.8;; esac
  [[ -n "$version" ]] && echo "`basename "$1"` contains classes compiled with JDK version $version."
}

Vous pouvez les coller pour une utilisation unique ou les ajouter à ~/.bash_aliases ou ~/.bashrc. Les résultats ressemblent à quelque chose comme:

$ jar_jdk_version poi-ooxml-3.5-FINAL.jar
49

et

$ print_jar_jdk_version poi-ooxml-3.5-FINAL.jar
poi-ooxml-3.5-FINAL.jar contains classes compiled with JDK version 1.5.

EDIT Comme le souligne jackrabbit , vous ne pouvez pas compter à 100% sur le manifeste pour vous dire quelque chose d'utile. Si tel était le cas, vous pourriez l'extraire dans votre shell UNIX préféré avec unzip:

$ unzip -pa poi-ooxml-3.5-FINAL.jar META-INF/MANIFEST.MF
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.1
Created-By: 11.3-b02 (Sun Microsystems Inc.)
Built-By: yegor
Specification-Title: Apache POI
Specification-Version: 3.5-FINAL-20090928
Specification-Vendor: Apache
Implementation-Title: Apache POI
Implementation-Version: 3.5-FINAL-20090928
Implementation-Vendor: Apache

Ce fichier .jar n’a rien d’utile dans le manifeste concernant les classes contenues.

2
Joe Liversedge

Chaque fichier de classe a un numéro de version incorporé pour le niveau de code d'octet que la machine virtuelle Java utilise pour voir si elle aime ou non cette partie de code d'octet particulière. Il s'agit de 48 pour Java 1,4, 49 pour Java 1,5 et 50 pour Java 6.

Il existe de nombreux compilateurs pouvant générer du code octet à chaque niveau, javac utilise l'option "-target" pour indiquer le niveau de code octet à générer, et le javac Java 6 peut générer du code octet pour au moins 1.4, 1.5 et 6. Je ne crois pas que le compilateur insère quoi que ce soit qui puisse identifier le compilateur lui-même, ce que je pense que vous demandez. De plus, le compilateur Eclipse est de plus en plus utilisé, car il s’agit d’un seul fichier jar pouvant être exécuté avec le JRE uniquement.

Dans un fichier jar, il existe généralement de nombreuses classes, chacune étant indépendante, vous devez donc examiner toutes les classes du fichier jar pour être certain des caractéristiques du contenu.

2

Pour développer Jonathon Faust et McDowell réponses: Si vous utilisez un système basé sur * nix, vous pouvez utiliser od (l'un des premiers programmes Unix1 devrait être disponible pratiquement partout) pour interroger le fichier .class sur un niveau binaire:

od -An -j7 -N1 -t dC SomeClassFile.class

Cela produira les valeurs entières familières, par ex. 50 pour Java 5, 51 pour Java 6 et ainsi de suite.

1 Citation de https://en.wikipedia.org/wiki/Od_ (Unix)

1
zb226

Pour faire suite à la réponse de @David J. Liszewski, j’ai exécuté les commandes suivantes pour extraire le manifeste du fichier jar sous Ubuntu:

# Determine the manifest file name:
$ jar tf LuceneSearch.jar | grep -i manifest
META-INF/MANIFEST.MF

# Extract the file:
$ Sudo jar xf LuceneSearch.jar META-INF/MANIFEST.MF

# Print the file's contents:
$ more META-INF/MANIFEST.MF
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.2
Created-By: 1.7.0_25-b30 (Oracle Corporation)
Main-Class: org.wikimedia.lsearch.config.StartupManager
1
Alan

Bien souvent, vous pouvez regarder des fichiers jar entiers ou des fichiers war contenant beaucoup de fichiers jar en plus d'eux-mêmes.

Comme je ne voulais pas vérifier manuellement chaque classe, j'ai écrit un programme Java pour le faire:

https://github.com/Nthalk/WhatJDK

./whatjdk some.war
some.war:WEB-INF/lib/xml-apis-1.4.01.jar contains classes compatible with Java1.1
some.war contains classes compatible with Java1.6

Bien que cela ne dise pas avec quoi la classe a été compilée, cela détermine ce que JDK sera capable de charger les classes, ce qui est probablement ce que vous vouliez commencer.

1
Nthalk

Vous archivez dans le fichier manifeste d'un exemple de fichier jar:

Manifest-Version: 1.0 Création-Par: 1.6.0 (IBM Corporation)

0
amoljdv06

J'ai également écrit mon propre script bash pour vider la version Java requise par tous les fichiers jars passés en ligne de commande ... La mienne est un peu approximative, mais fonctionne pour moi ;-)

exemple d'utilisation

$ jar_dump_version_of_jvm_required.sh *.jar
JVM VERSION REQUIRED: 46.0, /private/tmp/jars/WEB-INF/lib/json-simple-1.1.jar
JVM VERSION REQUIRED: 49.0, /private/tmp/jars/WEB-INF/lib/json-smart-1.1.1.jar
JVM VERSION REQUIRED: 50.0, /private/tmp/jars/WEB-INF/lib/jsontoken-1.0.jar
JVM VERSION REQUIRED: 50.0, /private/tmp/jars/WEB-INF/lib/jsr166y-1.7.0.jar

jar_dump_version_of_jvm_required.sh

#!/bin/bash

DIR=$(PWD)
function show_help()
{
  ME=$(basename $0)
  IT=$(cat <<EOF

  Dumps the version of the JVM required to run the classes in a jar file

  usage: $ME JAR_FILE

  e.g. 

  $ME myFile.jar    ->  VERSION: 50.0     myFile.jar

  Java versions are:
  54 = Java 10
  53 = Java 9
  52 = Java 8
  51 = Java 7
  50 = Java 6
  49 = Java 5
  48 = Java 1.4
  47 = Java 1.3
  46 = Java 1.2
  45.3 = Java 1.1

EOF
  )
  echo "$IT"
  exit
}

if [ "$1" == "help" ]
then
  show_help
fi
if [ -z "$1" ]
then
  show_help
fi

function unzipJarToTmp()
{
  JAR=$1
  CLASS_FILE=$(jar -tf "$JAR" | grep \.class$ | grep -v '\$' | head -n1 | awk '{print $NF}')
  OUT_FILE="$CLASS_FILE"
  #echo "J=$JAR C=$CLASS_FILE O=$OUT_FILE"
  jar xf "$JAR" "$CLASS_FILE"

  MAJOR=$(javap -v "$OUT_FILE" 2>&1 | grep major | awk -F' ' '{print $3'})
  MINOR=$(javap -v "$OUT_FILE" 2>&1 | grep minor | awk -F' ' '{print $3'})
  if [ -z "$MAJOR" ]
  then
    echo "JVM VERSION REQUIRED: NA as no classes in $JAR"
  else
    echo "JVM VERSION REQUIRED: $MAJOR.$MINOR, $JAR"
  fi
}

# loop over cmd line args
for JAR in "$@"
do
  cd "$DIR"
  JAR_UID=$(basename "$JAR" | sed s/.jar//g)
  TMPDIR=/tmp/jar_dump/$JAR_UID/
  mkdir -p "$TMPDIR"
  JAR_ABS_PATH=$(realpath $JAR)

  cd "$TMPDIR"

  #echo "$JAR_ABS_PATH"
  unzipJarToTmp "$JAR_ABS_PATH"
  #sleep 2
done
0
Brad Parks