Skip to content
Projeler
Gruplar
Parçacıklar
Yardım
Yükleniyor...
Oturum aç / Kaydol
Gezinmeyi değiştir
I
inary
Proje
Proje
Ayrıntılar
Etkinlik
Cycle Analytics
Depo (repository)
Depo (repository)
Dosyalar
Kayıtlar (commit)
Dallar (branch)
Etiketler
Katkıda bulunanlar
Grafik
Karşılaştır
Grafikler
Konular (issue)
1
Konular (issue)
1
Liste
Pano
Etiketler
Kilometre Taşları
Birleştirme (merge) Talepleri
0
Birleştirme (merge) Talepleri
0
CI / CD
CI / CD
İş akışları (pipeline)
İşler
Zamanlamalar
Grafikler
Paketler
Paketler
Wiki
Wiki
Parçacıklar
Parçacıklar
Üyeler
Üyeler
Collapse sidebar
Close sidebar
Etkinlik
Grafik
Grafikler
Yeni bir konu (issue) oluştur
İşler
Kayıtlar (commit)
Konu (issue) Panoları
Kenar çubuğunu aç
SulinOS
inary
Commits
82c09455
Kaydet (Commit)
82c09455
authored
Ock 18, 2018
tarafından
Suleyman Poyraz
Dosyalara gözat
Seçenekler
Dosyalara Gözat
İndir
Eposta Yamaları
Sade Fark
When packaging we need a dependency controller for brocken runtime dependencies.
So Iwrite this library for controlling dependencies
üst
38b918ac
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
344 additions
and
0 deletions
+344
-0
pkgconfig.py
inary/analyzer/pkgconfig.py
+344
-0
No files found.
inary/analyzer/pkgconfig.py
0 → 100644
Dosyayı görüntüle @
82c09455
# -*- coding: utf-8 -*-
#
# Copyright (C) 2018, Suleyman POYRAZ (Zaryob)
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""dependency analyzer from pkgconfig .pc and .ldd files when package building"""
import
os
import
re
import
sys
import
glob
import
magic
import
shutil
import
fnmatch
import
tempfile
import
optparse
import
itertools
import
subprocess
#Inary functions
import
inary
import
inary.context
as
ctx
#Gettext
import
gettext
__trans
=
gettext
.
translation
(
'inary'
,
fallback
=
True
)
_
=
__trans
.
gettext
INSTALLDB
=
inary
.
db
.
installdb
.
InstallDB
()
COMPONENTDB
=
inary
.
db
.
componentdb
.
ComponentDB
()
CONSTANTS
=
inary
.
constants
.
Constants
()
REPODB
=
inary
.
db
.
repodb
.
RepoDB
class
LDD
:
def
__init__
(
self
,
packages
,
directory
,
component
,
installed_list
=
True
,
systembase
=
False
,
systemdevel
=
False
):
self
.
systembase
=
systembase
self
.
systemdevel
=
systemdevel
self
.
packages
=
package
self
.
component
=
component
# If directory is used as option
if
directory
:
for
root
,
dirs
,
files
in
os
.
walk
(
directory
):
for
data
in
files
:
if
data
.
endswith
(
".inary"
):
self
.
packages
.
append
(
os
.
path
.
join
(
root
,
data
))
# check for components, like system.base, tex.language, etc.
if
self
.
component
:
for
repo
in
RepoDB
.
list_repos
():
if
COMPONENTDB
.
has_component
(
component
):
self
.
packages
.
extend
(
COMPONENTDB
.
get_packages
(
component
,
repo
))
# check for all packages installed on the machine
if
installedlist
:
self
.
packages
.
extend
(
INSTALLDB
.
list_installed
())
def
find_dependencies_on_pkgconfig
(
self
):
pkgconfig_list
=
[]
used_inary
=
False
# Do not check for a inary binary file and for a package installed on the system
for
package
in
self
.
packages
:
# Check loop for .inary files
if
package
.
endswith
(
".inary"
):
used_inary
=
True
package_inary
=
inary
.
package
.
Package
(
package
)
package_meta
=
package_inary
.
get_metadata
()
package_name
=
package_meta
.
package
.
name
# Gather runtime dependencies directly from the metadata.xml
package_deps
=
[
dep
.
name
()
for
dep
in
package_meta
.
package
.
runtimeDependencies
()]
# Contains extracted package content
package_tempdir
=
tempfile
.
mkdtemp
(
prefix
=
os
.
path
.
basename
(
sys
.
argv
[
0
])
+
'-'
)
package_inary
.
extract_install
(
package_tempdir
)
# Get results from objdump,ldd,etc...
result_dependencies
,
result_broken
,
result_unused
,
result_undefined
,
result_runpath
=
\
self
.
generate_result
(
package_name
,
package_tempdir
)
# Look for intersections of the packages(i.e. do not include system.base packages)
# result_lists is a iteration object which contains tuples of length 3
result_lists
=
self
.
check_intersections
(
result_dependencies
,
package_deps
,
package_name
,
self
.
systembase
,
self
.
systemdevel
)
# Delete the created temporary directory
if
package_tempdir
.
startswith
(
"/tmp/"
):
shutil
.
rmtree
(
package_tempdir
)
#add a touple
pkgconfig_list
.
append
((
result_broken
,
result_unused
,
result_undefined
,
result_lists
,
result_runpath
,
package_name
))
# Check for a installed package in the system
elif
package
in
INSTALLDB
.
list_installed
():
if
used_inary
:
raise
Error
(
"You've checked for a inary file before
\n
Please do not check for a installed package and inary file at the same time"
)
else
:
package_name
=
package
# Gather runtime dependencies directly from the database of installed packages
package_deps
=
[
dep
.
name
()
for
dep
in
INSTALLDB
.
get_package
(
package
)
.
runtimeDependencies
()]
package_tempdir
=
False
# There is no need of temporary directory, hence we look for files that are installed
# Same functions in the above part. You can read them
result_dependencies
,
result_broken
,
result_unused
,
result_undefined
,
result_runpath
=
\
self
.
generate_result
(
package_name
,
package_tempdir
)
result_lists
=
check_intersections
(
result_dependencies
,
package_deps
,
package_name
,
self
.
systembase
,
self
.
systemdevel
)
pkgconfig_list
.
append
((
result_broken
,
result_unused
,
result_undefined
,
result_lists
,
result_runpath
,
package_name
))
else
:
raise
Error
(
"'
%
s' is not a valid .inary file or an installed package"
%
package
)
return
pkgconfig_list
def
process_ldd
(
self
,
objdump_needed
,
ldd_output
,
ldd_unused
,
ldd_undefined
):
'''Process the ldd outputs. And return a simple path only lists'''
# result_needed = (all shared libary dependencies) - (needed shared library gathered from objdump)
# result_broken = broken libraries that are not available at their place
# result_unused = unused direct dependencies
# result_undefined = undefined symbol errors
result_unused
=
[]
result_undefined
=
[]
result_broken
=
[]
result_main_ldd
=
{}
result_needed
=
[]
for
line
in
ldd_unused
.
replace
(
"
\t
"
,
""
)
.
split
(
"
\n
"
):
if
not
line
==
""
and
not
"Unused"
in
line
:
result_unused
.
append
(
line
.
strip
())
for
line
in
ldd_undefined
.
replace
(
"
\t
"
,
""
)
.
split
(
"
\n
"
):
if
line
.
startswith
(
"undefined symbol:"
):
result_undefined
.
append
(
re
.
sub
(
"^undefined symbol: (.*)
\
((.*)
\
)$"
,
"
\\
1"
,
line
))
for
line
in
ldd_output
:
if
"=>"
in
line
:
# Filter these special objects
if
"linux-gate"
in
line
or
\
"ld-linux"
in
line
or
"linux-vdso"
in
line
:
continue
so_name
,
so_path
=
line
.
split
(
"=>"
)
if
"not found"
in
so_path
:
# One of the dynamic dependencies is missing
result_broken
.
append
(
so_name
.
strip
())
else
:
result_main_ldd
[
so_name
.
strip
()]
=
so_path
.
split
(
" ("
)[
0
]
.
strip
()
for
obj
in
objdump_needed
:
# Find the absolute path of libraries from their SONAME's
if
obj
in
result_main_ldd
:
result_needed
.
append
(
os
.
popen
(
"readlink -f
%
s"
%
result_main_ldd
[
obj
])
.
read
()
.
strip
())
else
:
result_needed
.
append
(
obj
)
return
(
result_needed
,
result_broken
,
result_unused
,
result_undefined
)
def
check_objdump
(
self
,
processed_needed
,
package_elf_files
,
package_name
):
'''check the objdump needed libraries with the ldd libraries
the libraries that are needed can be used for dependencies'''
result_needed
=
[]
# check if the libraries are shipped with the package
# then associate each library(with his package_name) with the given elf_file
for
obj
in
processed_needed
:
if
obj_dump
in
package_elf_files
:
# file is shipped within this package
dependency_name
=
package_name
else
:
# search for the package name (i.e: inary sf /usr/lib/*.so )
# the library may not exist, thus adding an exception is welcome
try
:
dependency_name
=
inary
.
api
.
search_file
(
obj_dump
)[
0
][
0
]
except
IndexError
:
dependency_name
=
"broken"
ctx
.
ui
.
info
(
"
%
s (probably broken dependency)"
%
needed
)
result_needed
.
append
((
obj_dump
,
dependency_name
))
return
result_needed
def
check_pc_files
(
self
,
pc_file
):
'''check for .pc files created by pkgconfig and shipped with the package
these .pc files have requirements tags that can be used for dependencies'''
result_needed
=
[]
requires
=
set
(
os
.
popen
(
"pkg-config --print-requires --print-requires-private
%
s | gawk '{ print $1 }'"
%
\
os
.
path
.
basename
(
pc_file
)
.
replace
(
".pc"
,
""
))
.
read
()
.
split
(
"
\n
"
)[:
-
1
])
for
require
in
requires
:
require_file
=
"/usr/share/pkgconfig/
%
s.pc"
%
require
if
not
os
.
path
.
exists
(
require_file
):
require_file
=
"/usr/lib/pkgconfig/
%
s.pc"
%
require
try
:
dependency_name
=
inary
.
api
.
search_file
(
require_file
)[
0
][
0
]
except
IndexError
:
dependency_name
=
"broken"
result_needed
.
append
((
require_file
,
dependency_name
))
return
result_needed
def
check_intersections
(
self
,
result_dependencies
,
package_deps
,
package_name
):
'''eliminate system base and system devel packages and self written deps'''
# get system.base and system.devel packages
systembase_packages
=
[]
systemdevel_packages
=
[]
for
repo
in
REPODB
.
list_repos
():
for
component
in
COMPONENTDB
.
list_components
(
repo
):
if
component
==
"system.base"
:
systembase_packages
.
extend
(
COMPONENTDB
.
get_packages
(
'system.base'
,
repo
))
if
component
==
"system.devel"
:
systemdevel_packages
.
extend
(
COMPONENTDB
.
get_packages
(
'system.devel'
,
repo
))
# look for packages that are system.base but are written as dependency
# mark them with "*"
result_must_removed
=
list
(
set
(
package_deps
)
&
set
(
systembase_packages
))
for
deps
in
package_deps
:
if
deps
in
result_must_removed
:
package_deps
[
package_deps
.
index
(
deps
)]
=
"
%
s (base)"
%
deps
# look for packages that are system.devel but are written as dependency
# mark them with "*"
result_must_removed
=
list
(
set
(
package_deps
)
&
set
(
systemdevel_packages
))
for
deps
in
package_deps
:
if
deps
in
result_must_removed
:
package_deps
[
package_deps
.
index
(
deps
)]
=
"
%
s (devel)"
%
deps
# extract the dependency package names and store them in result_deps
# dependencies tagged as broken or given itself are eliminated
dependencies
=
set
()
result_deps
=
[]
for
elf_files
,
paths_and_deps
in
list
(
result_dependencies
.
items
()):
for
data
in
paths_and_deps
:
if
not
data
[
1
]
==
"broken"
and
not
data
[
1
]
==
package_name
:
result_deps
.
append
(
data
[
1
])
# remove packages that belong to system.base component
if
not
self
.
systembase
:
result_deps
=
list
(
set
(
result_deps
)
-
set
(
systembase_packages
))
if
not
self
.
systemdevel
and
package_name
.
endswith
(
'-devel'
):
result_deps
=
list
(
set
(
result_deps
)
-
set
(
systemdevel_packages
))
if
self
.
systemdevel
or
self
.
systembase
:
result_must_removed
=
list
(
set
(
result_deps
)
&
set
(
systembase_packages
))
for
deps
in
result_deps
:
if
deps
in
result_must_removed
:
result_deps
[
result_deps
.
index
(
deps
)]
=
"
%
s (base)"
%
deps
result_must_removed
=
list
(
set
(
result_deps
)
&
set
(
systemdevel_packages
))
for
deps
in
result_deps
:
if
deps
in
result_must_removed
:
result_deps
[
result_deps
.
index
(
deps
)]
=
"
%
s (devel)"
%
deps
result_deps
=
list
(
set
(
result_deps
))
# remove packages that already are written in metadata.xml (runtime dependencies written in pspec.xml)
result_section
=
list
(
set
(
result_deps
)
-
set
(
package_deps
))
# create a sorted iteration object of the final results variables
# the lists may have variable lengths, thus we fill the smallers one with empty strings.
cmp_func
=
lambda
x
,
y
:
len
(
x
)
-
len
(
y
)
result_lists
=
itertools
.
zip_longest
(
sorted
(
list
(
set
(
package_deps
)),
cmp
=
cmp_func
),
sorted
(
result_deps
,
cmp
=
cmp_func
),
sorted
(
result_section
,
cmp
=
cmp_func
),
fillvalue
=
""
)
return
result_lists
def
generate_result
(
self
,
package_name
,
package_dir
):
'''execute ldd on elf files and returns them'''
#There maybe more than one elf file, check for each one
_dependencies
=
{}
_unused
=
{}
_undefined
=
{}
_broken
=
None
_runpath
=
{}
ld_library_paths
=
set
()
package_elf_files
=
[]
# Two options are available. Checking for a inary file or an installed package in the database
if
package_dir
:
package_files
=
os
.
popen
(
"find
%
s"
%
package_dir
)
.
read
()
.
strip
()
.
split
(
"
\n
"
)
package_pc_files
=
glob
.
glob
(
"
%
s/usr/*/pkgconfig/*.pc"
%
package_dir
)
else
:
package_files
=
set
([
"/
%
s"
%
file_name
.
path
\
for
file_name
in
INSTALLDB
.
get_files
(
package_name
)
.
list
])
package_pc_files
=
set
([
os
.
path
.
realpath
(
"/
%
s"
%
file_name
.
path
)
\
for
file_name
in
INSTALLDB
.
get_files
(
package_name
)
.
list
\
if
fnmatch
.
fnmatch
(
file_name
.
path
,
"*/pkgconfig/*.pc"
)])
for
package_file
in
package_files
:
package_file_info
=
magic
.
from_file
(
package_file
)
#Return file type
if
"LSB shared object"
in
package_file_info
:
package_elf_files
.
append
(
os
.
path
.
realpath
(
package_file
))
elif
"LSB executable"
in
package_file_info
:
package_elf_files
.
append
(
package_file
)
# Add library paths for unpacked inary files
if
package_dir
:
for
elf_file
in
package_elf_files
:
if
elf_file
.
endswith
(
".so"
)
or
".so."
in
elf_file
:
ld_library_paths
.
add
(
os
.
path
.
dirname
(
elf_file
))
os
.
environ
.
update
({
'LD_LIBRARY_PATH'
:
":"
.
join
(
ld_library_paths
)})
for
elf_file
in
package_elf_files
:
ldd_output
=
subprocess
.
Popen
([
"ldd"
,
elf_file
],
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
STDOUT
,
env
=
os
.
environ
)
.
communicate
()[
0
]
.
strip
()
.
split
(
"
\n
"
)
ldd_unused
,
ldd_undefined
=
subprocess
.
Popen
([
"ldd"
,
"-u"
,
"-r"
,
elf_file
],
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
,
env
=
os
.
environ
)
.
communicate
()
runpath
=
subprocess
.
Popen
([
"chrpath"
,
"-l"
,
elf_file
],
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
STDOUT
,
env
=
os
.
environ
)
.
communicate
()[
0
]
.
strip
()
.
split
(
": "
)
objdump_needed
=
[
line
.
strip
()
.
split
()[
1
]
for
line
in
\
os
.
popen
(
"objdump -p
\"
%
s
\"
| grep 'NEEDED'"
%
elf_file
)
.
readlines
()]
# Process the various ldd and objdump outputs
processed_needed
,
processed_broken
,
processed_unused
,
processed_undefined
=
\
self
.
process_ldd
(
objdump_needed
,
ldd_output
,
ldd_unused
,
ldd_undefined
)
# association with each single elf file
_unused
.
update
(
dict
([(
elf_file
,
processed_unused
)]))
_undefined
.
update
(
dict
([(
elf_file
,
processed_undefined
)]))
_runpath
.
update
(
dict
([(
elf_file
,
runpath
)]))
_broken
=
processed_broken
_dependencies
[
elf_file
]
=
self
.
check_objdump
(
processed_needed
,
package_elf_files
,
package_name
)
# Check for .pc files
for
pc_file
in
package_pc_files
:
_dependencies
[
pc_file
]
=
check_pc_files
(
pc_file
)
return
(
_dependencies
,
_broken
,
_unused
,
_undefined
,
_runpath
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment