Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SCons: Decode stdout from subprocess #2201

Merged
merged 13 commits into from
Aug 23, 2019
Merged

SCons: Decode stdout from subprocess #2201

merged 13 commits into from
Aug 23, 2019

Conversation

uklotzde
Copy link
Contributor

@uklotzde uklotzde commented Jul 8, 2019

Try to fix some issues with SCons 3 reported here: #1356 (comment)

build/mixxx.py Outdated
@@ -213,15 +213,15 @@ def __init__(self, target, machine, build, toolchain, available_features):
# Now that environment variables have been read, we can detect the compiler.
import subprocess
process = subprocess.Popen("%s %s" %(self.env['CC'], '--version'), stdout=subprocess.PIPE, shell=True) # nosec
(stdout, stderr) = process.communicate()
stdout = process.communicate()[0].rstrip().decode()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might make sense to use subprocess.check_output() instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the hint!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just read that process.run() is recommended instead of subprocess.check_output() since 3.5? I'm not able to decide what to choose here, considering that we need to support a variety of Python versions. Maybe someone else can take over and fix all those issues?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you only want to check the content of stdout anyway, there's no actual advantage from using subprocess.run() other than being hip and modern. On the other hand, we'd lose compatibility with all Python versions < 3.5 (subprocess.check_output even works on Python 2.7).

build/mixxx.py Outdated
@@ -213,15 +213,15 @@ def __init__(self, target, machine, build, toolchain, available_features):
# Now that environment variables have been read, we can detect the compiler.
import subprocess
process = subprocess.Popen("%s %s" %(self.env['CC'], '--version'), stdout=subprocess.PIPE, shell=True) # nosec
Copy link
Member

@Holzhaus Holzhaus Jul 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shell=True in combination with user input opens the door for command injections, e.g. if CC is set to rm -rf /; gcc.
Instead, something like this should be used:

process = subprocess.Popen([self.env['CC'], '--version'], stdout=subprocess.PIPE)`

# or:
stdout = subprocess.check_output([self.env['CC'], '--version'])`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shell=True argument has been added just recently by the NixOS build support, not sure why.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@poelzi has explained it here:
https://github.com/mixxxdj/mixxx/pull/2144/files#r291847013
Unfortunately the comment was not added to the code. Maybe we can add a comment here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case IMHO it's still undesirable to invoke a shell because we don't know what crazy non-standard $SHELL the user is using. shlex.split() can be used to split the content of CC env var, so that shell=True is not needed.

stdout = subprocess.check_output([*shlex.split(self.env['CC']), '--version'])`

# or the longer version
cmd = shlex.split(self.env['CC'])
cmd.append('--version')
stdout = subprocess.check_output(cmd)`

@uklotzde uklotzde changed the title SCons: Decode stdout from subprocess [WiP] SCons: Decode stdout from subprocess Jul 8, 2019
@uklotzde
Copy link
Contributor Author

@Holzhaus

  • Is .decode(sys.stdout.encoding) required?
  • Replacing Popen() with check_output() in depends.py: find_framework_libdir fails with "coercing to Unicode: need string or buffer, NoneType found"

@uklotzde uklotzde changed the title [WiP] SCons: Decode stdout from subprocess SCons: Decode stdout from subprocess Jul 14, 2019
@uklotzde uklotzde added this to the 2.3.0 milestone Jul 15, 2019
@Holzhaus
Copy link
Member

@uklotzde Sorry for the late answer, I'm currently traveling. Since subprocess.check_output() returns bytes (not str), it is necessary to decode the return value. However, .decode() without an explicit encoding should suffice.

I couldn't reproduce the issue from your second point, but have a look at this example: uklotzde/mixxx@scons...Holzhaus:scons
If you need to retain python 2.7 compatibility, use OSError instead of FileNotFoundError.

@rrrapha
Copy link
Contributor

rrrapha commented Jul 20, 2019

Note that this changes the behaviour of the compiler check. It fails if a compiler doesn't support --version.
For example, the following throws an error now (and did not before):

$ export CC=
$ scons

Not sure if it's a real problem..

@daschuer
Copy link
Member

I can confirm that this still builds on my Ubuntu Xenial using Scons v2.4.1.

@uklotzde
Copy link
Contributor Author

I'm not experienced enough with Python and will not change this code again. Please take over if you know a better way.

@Holzhaus
Copy link
Member

@uklotzde I made some smaller fixes and improvements:
uklotzde/mixxx@scons...Holzhaus:scons

Build fine for me on Linux now (I had some problems with the pkg-config invocation so that Qt couldn't be found.)

@uklotzde
Copy link
Contributor Author

The build with Scons 3.0.4 / Python 2.7 fails now:

python2-scons

TypeError: glob() got an unexpected keyword argument 'recursive':
  File "/run/media/uk/Private/tmp/mixxx/SConstruct", line 82:
    SConscript('SConscript.env', variant_dir=build.build_dir, duplicate=0)
  File "/usr/lib/python2.7/site-packages/SCons/Script/SConscript.py", line 667:
    return method(*args, **kw)
  File "/usr/lib/python2.7/site-packages/SCons/Script/SConscript.py", line 604:
    return _SConscript(self.fs, *files, **subst_kw)
  File "/usr/lib/python2.7/site-packages/SCons/Script/SConscript.py", line 285:
    call_stack[-1].globals)
  File "/run/media/uk/Private/tmp/mixxx/SConscript.env", line 106:
    dep_sources = dependency.sources(build)
  File "/run/media/uk/Private/tmp/mixxx/build/depends.py", line 718:
    sources = glob.glob('src/**/*.cpp', recursive=True)

Unfortunately the build fails due to another reason when using the same SCons version on Python 3.7 as reported on Launchpad:

python3-scons

[MOC] src/controllers/midi/portmidienumerator.h
[CXX] src/controllers/midi/portmidicontroller.cpp
[CXX] lin64_build/src/controllers/midi/moc_portmidienumerator.cc
[CXX] src/controllers/midi/portmidienumerator.cpp
[CXX] src/effects/lv2/lv2backend.cpp
[CXX] src/effects/lv2/lv2effectprocessor.cpp
[CXX] src/effects/lv2/lv2manifest.cpp
[MOC] src/effects/lv2/lv2backend.h
[CXX] src/encoder/encoderopus.cpp
[CXX] lin64_build/src/effects/lv2/moc_lv2backend.cc
[CXX] src/engine/bufferscalers/enginebufferscalerubberband.cpp
[CXX] src/engine/bufferscalers/enginebufferscalest.cpp
[MOC] src/engine/bufferscalers/enginebufferscalerubberband.h
[CXX] lin64_build/src/engine/bufferscalers/moc_enginebufferscalerubberband.cc
[MOC] src/engine/bufferscalers/enginebufferscalest.h
[CXX] lin64_build/src/engine/bufferscalers/moc_enginebufferscalest.cc
[MOC] src/engine/controls/vinylcontrolcontrol.h
[CXX] src/engine/controls/vinylcontrolcontrol.cpp
[CXX] lin64_build/src/engine/controls/moc_vinylcontrolcontrol.cc
[MOC] src/engine/sidechain/shoutconnection.h
[CXX] lin64_build/src/engine/sidechain/moc_shoutconnection.cc
[CXX] src/engine/sidechain/shoutconnection.cpp
[UIC5] src/preferences/dialog/dlgprefbroadcastdlg.ui
[CXX] src/preferences/dialog/dlgprefbroadcast.cpp
[CXX] src/preferences/dialog/dlgpreflv2.cpp
In file included from src/preferences/dialog/dlgpreflv2.cpp:8:
src/preferences/dialog/dlgpreflv2.h:7:10: fatal error: preferences/dialog/ui_dlgpreflv2dlg.h: No such file or directory
    7 | #include "preferences/dialog/ui_dlgpreflv2dlg.h"
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
scons: *** [lin64_build/src/preferences/dialog/dlgpreflv2.o] Error 1
scons: building terminated because of errors.

@Holzhaus
Copy link
Member

@uklotzde Sorry, I'm currently on my old and slow laptop, so I just Ctrl-C'ed after the configuration stage.

glob.glob only supports recursive mode since Python 3.5. Just revert uklotzde@dfef9c7 for now. This should also fix the other issue for now (but IMHO the root cause is the broken dlgpreflv2.cpp file, which should either be fixed or removed).
I dropped that commit from my branch.
We can add it again in approximately 4 month and 20 days, when Python 2 will finally be retired ;-)

I also had to add another commit to fix compilation with SCons 3.1.1:
uklotzde@d3562f1

I can confirm that compilation works with Scons 3.1.1 and Python 3.7.

@Be-ing
Copy link
Contributor

Be-ing commented Aug 11, 2019

IMHO the root cause is the broken dlgpreflv2.cpp file, which should either be fixed or removed

I intend to eventually remove that as part of #1705, but that PR still needs a lot of work before it is ready for merge so don't count on that happening soon.

@Holzhaus
Copy link
Member

IMHO the root cause is the broken dlgpreflv2.cpp file, which should either be fixed or removed

I intend to eventually remove that as part of #1705, but that PR still needs a lot of work before it is ready for merge so don't count on that happening soon.

Ok, don't worry. It's not time critical since I remove the commit (that replaced the hardcoded sources with globbing) anyway. If we add it again, we could add a blacklist to exclude files instead, which would still be shorter than the hardcoded whitelist.

@uklotzde Can we merge this or are there issues with older SCons/Python versions? Currently, compilation is completely broken for me without these changes (Scons 3.11, Python 3.7).

uklotzde and others added 3 commits August 14, 2019 21:46
This re-adds 'utf-8' to bytes.decode() calls and replaces
FileNotFoundError with OSError to restore compatibility with Python 2.7.

When Python 2.7 finally reaches EOL we can simply revert this commit.
@Holzhaus
Copy link
Member

@uklotzde I had a look at the TravisCI build log. Looks like it failed due to missing Python 2.7 compatibilty. Here are some more fixes that restores it: uklotzde/mixxx@scons...Holzhaus:scons

Works with SCons 3.0.4/Python 2.7.16 and SCons 3.1.1/Python 3.7.4 (tested on Linux/AMD64).

@Holzhaus
Copy link
Member

By the way, let's merge this ASAP. It's getting annoying that I need to apply/stash Scons-3.1-related fixes on every branch that I'm working on ;-)

@uklotzde
Copy link
Contributor Author

Let's wait until the CI build have successfully completed. Since no one else takes care of this rather urgent issues I will merge it asap!! Thanks for providing all these patches.

I will try a clean Python 3 / SCons 3.0.4 build on fc30, although I don't expect it to succeed.

@uklotzde
Copy link
Contributor Author

As expected, a clean build with Python 3 / SCons 3.0.4 still fails:

[CXX] src/analyzer/trackanalysisscheduler.cpp
In file included from src/library/analysisfeature.h:16,
                 from src/library/library.h:18,
                 from src/analyzer/trackanalysisscheduler.cpp:3:
src/library/dlganalysis.h:11:10: fatal error: library/ui_dlganalysis.h: No such file or directory
   11 | #include "library/ui_dlganalysis.h"
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
scons: *** [lin64_build/src/analyzer/trackanalysisscheduler.o] Error 1

I delete lin64_build/, .sconf_temp/ and .sconsign.dblite for a clean build.

@Holzhaus
Copy link
Member

@uklotzde Sorry, I used scons -c to clean up. Looks like that doesn't suffice though. I'm getting the same error as you when using Scons 3.1.1 and Python 3.7.4.

However, this doesn't seem to be related to this PR, since it also happens when I try to build the current master branch with this patch:

$ git stash show -U
diff --git a/build/mixxx.py b/build/mixxx.py
index b9501ee81a..456f079685 100644
--- a/build/mixxx.py
+++ b/build/mixxx.py
@@ -214,8 +214,8 @@ class MixxxBuild(object):
         import subprocess
         process = subprocess.Popen("%s %s" %(self.env['CC'], '--version'), stdout=subprocess.PIPE, shell=True) # nosec
         (stdout, stderr) = process.communicate()
-        self.compiler_is_gcc = 'gcc' in stdout.lower()
-        self.compiler_is_clang = 'clang' in stdout.lower()
+        self.compiler_is_gcc = 'gcc' in stdout.decode().lower()
+        self.compiler_is_clang = 'clang' in stdout.decode().lower()

         # Determine the major compiler version (only GCC)
         if self.compiler_is_gcc:
@@ -225,7 +225,7 @@ class MixxxBuild(object):
             gcc_version = stdout
             # If match is None we don't know the version.
             if not gcc_version is None:
-                version_split = gcc_version.split('.')
+                version_split = gcc_version.decode().split('.')
                 if version_split:
                     self.gcc_major_version = int(version_split[0])

I'll investigate.

@uklotzde
Copy link
Contributor Author

Definitely not caused by your changes. It's a known and long outstanding bug that we need to solve soon as Python 2 becomes deprecated:

https://bugs.launchpad.net/mixxx/+bug/1817742

Not sure if anyone is working on this already? I didn't get any notice from "andy-xyz" after he has assigned the bug to himself.

@uklotzde
Copy link
Contributor Author

Unfortunately macOS/clang build now fails:

Configuring Qt
Package Qt5Core was not found in the pkg-config search path.
Perhaps you should add the directory containing `Qt5Core.pc'
to the PKG_CONFIG_PATH environment variable
No package 'Qt5Core' found
ERROR:root:Unmet dependency: global name 'FileNotFoundError' is not defined

@Holzhaus
Copy link
Member

@uklotzde

Unfortunately macOS/clang build now fails

Sorry, I accidently grepped just build/mixxx.py instead of the whole build directory.

Definitely not caused by your changes. It's a known and long outstanding bug that we need to solve soon as Python 2 becomes deprecated:

https://bugs.launchpad.net/mixxx/+bug/1817742

Not sure if anyone is working on this already? I didn't get any notice from "andy-xyz" after he has assigned the bug to himself.

I found the issue. Python's map() function is used to invoke uic for each *.ui file. In Python 3, map() returns an iterator which is evaluted on-demand (i.e. the next element is read) rather than when map() is called. This can save memory and computation time, but we're never reading map()'s results, so uic won't be executed at all.

Here are some more patches that should fix the remaining issues:
uklotzde/mixxx@scons...Holzhaus:scons

Works with SCons 3.0.4/Python 2.7.16 and SCons 3.1.1/Python 3.7.4 (and this time i rm -rf'ed lin64_build, .scons_temp and .sconsign.dblite instead of using scons -c :D).

@uklotzde
Copy link
Contributor Author

It just works now!! That was awesome, Jan 👍

I'm always afraid if we need to touch this brittle and hand-crafted build system. Let's wait for the CI builds.

@@ -197,15 +199,16 @@ def uic(build):
@staticmethod
def find_framework_libdir(qtdir):
# Try pkg-config on Linux
import sys
if sys.platform.startswith('linux') or sys.platform.find('bsd') >= 0:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we musn't remove this line, because it makes the MacOS build fail...

@uklotzde
Copy link
Contributor Author

Thank you for the detailed comments about how map() is supposed to work! This is not obvious for someone with hardly any Python knowledge like me.

@Be-ing
Copy link
Contributor

Be-ing commented Aug 23, 2019

I confirm this works on Fedora 30 with the scons-3 binary. 👍

@Be-ing
Copy link
Contributor

Be-ing commented Aug 23, 2019

I'm always afraid if we need to touch this brittle and hand-crafted build system.

FWIW, Qt is finally dropping their hand-crafted build system for Qt 6 and switching to CMake. We may consider doing the same soon.

@Holzhaus
Copy link
Member

Holzhaus commented Aug 23, 2019

@uklotzde Sorry that I need to bother you with so many changes, but I do not have MacOS and it looks like I can't I start builds on Travis CI manually without opening my own PR.

@Be-ing

I'm always afraid if we need to touch this brittle and hand-crafted build system.

FWIW, Qt is finally dropping their hand-crafted build system for Qt 6 and switching to CMake. We may consider doing the same soon.

Sounds like a plan 👍

@uklotzde
Copy link
Contributor Author

I don't want to switch to master for fc31/rawhide in RPM Fusion right now. But as soon as we branch out 2.3 we can remove the Python 2 workaround there.

@Be-ing
Copy link
Contributor

Be-ing commented Aug 23, 2019

Sounds like a plan +1

Are you volunteering to work on it? :)

@uklotzde
Copy link
Contributor Author

Don't mind. You are doing great work!

I think if you register your own account on Travis you can build individual branches from your GitHub repositories. I have both AppVeyor and Travis accounts, just in case. But most of the time I'm too lazy and open some [WiP] PR if it's almost ready.

@Be-ing
Copy link
Contributor

Be-ing commented Aug 23, 2019

I think if you register your own account on Travis you can build individual branches from your GitHub repositories. I have both AppVeyor and Travis accounts, just in case. But most of the time I'm too lazy and open some [WiP] PR if it's almost ready.

We have a wiki page for that.

@Holzhaus
Copy link
Member

I think if you register your own account on Travis you can build individual branches from your GitHub repositories. I have both AppVeyor and Travis accounts, just in case. But most of the time I'm too lazy and open some [WiP] PR if it's almost ready.

We have a wiki page for that.

Oops, I should RTFM in the future ;-)

Sounds like a plan +1

Are you volunteering to work on it? :)

I can't promise it, but the current SCons setup is super confusing so I probably should 😄

@Holzhaus
Copy link
Member

Looks like the MacOS build finally succeeded.

@uklotzde
Copy link
Contributor Author

The Windows build timed out, but otherwise it worked flawlessly. Let's wait another 30 minutes for the Ubuntu build, although I don't expect any new issues.

@uklotzde
Copy link
Contributor Author

LGTM.

Since @Holzhaus has actually done most of the work I think I'm entitled to merge my own PR ;) Finally we are ready for Python 3 / SCons 3!

@uklotzde uklotzde merged commit 9d02ef3 into mixxxdj:master Aug 23, 2019
@uklotzde uklotzde deleted the scons branch September 1, 2019 15:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants