Repository: SCons/scons

Issue:
 Empty man pages 
**Describe the bug**
scons-3.1.2, compiled from source on Fedora, produces empty man pages.

**Required information**
* Version of SCons
3.1.2
* Version of Python
3.7.6
* Which python distribution if applicable 
* How you installed SCons
Compiled from source code
* What Platform are you on?
Fedora 31
* How to reproduce your issue?
[Build log](https://koji.fedoraproject.org/koji/getfile?taskID=42410467&volume=DEFAULT&name=build.log)
* How you invoke scons:
`/usr/bin/python3 bootstrap.py --bootstrap_dir=build/scons`



Discussion:
I had just noticed this the other day. Will need to investigate the cause - whether it's the transition to Python 3 (the doc build on Py3 is "less mature" in some senses), or some error introduced along he way, or...   
After some checking it looks like something going wrong when processing with Python 3.  The same git checkout, the relevant snip from the build log when running doc build with Python 2:
```
__build_libxml2(["scons-time.1"], ["scons-time_db.xml"])
Note: Writing scons-time.1
__build_libxml2(["scons.1"], ["scons_db.xml"])
Element pubdate in namespace '' encountered in referenceinfo, but no template matches.
Warn: AUTHOR sect.: no personblurb|contrib for Steven Knight       
Note: AUTHOR sect.: see see http://docbook.sf.net/el/contrib       
Note: AUTHOR sect.: see see http://docbook.sf.net/el/personblurb
Note: Writing scons.1
__build_libxml2(["sconsign.1"], ["sconsign_db.xml"])
Note: Writing sconsign.1
scons: done building targets.
```
and with Python 3:
```
__build_lxml(["scons-time.1"], ["scons-time_db.xml"])
__build_lxml(["scons.1"], ["scons_db.xml"])
__build_lxml(["sconsign.1"], ["sconsign_db.xml"])
scons: done building targets.
```
Cute. `__build_lxml`:
```python
    try:
        with open(str(target[0]), "wb") as of:
            of.write(etree.tostring(result, pretty_print=True))
    except:
        pass
```
For the three manpage files, and for a couple of other files as well, `tostring` is returning `None`, which is why the manpages are empty. The input files in these cases are the same between a Py2 and a Py3 run.
A fix for this that works for me (on Fedora, even), replace the snip in the previous comment (this is in `SCons/Tool/docbook/__init__.py`) with letting lxml do the work itself:
```python
    result.write_output(str(target[0]))
```
Probably needs to be tested a few more places.

Don't know how @bdbaddog  would want to go about rolling this out, the code in master branch has moved well beyond 3.1.2...
It'll have to wait for the next release (4.0.0). 
Building SCons from source with a released version of SCons is an unusual flow.
It's meant to build the current source with the current source.

Also a note to @sagitter 
If you want to just run scons from a checked out git repo, you can just run via:
<sandbox_dir>/src/script/scons.py

At some point we'll get our setup.py properly update and then you'd be able to pip install (or install -e) from the sandbox. But we're not there yet.
I have to look this up and try to remember, but at the moment I'd say the main point of the "try"block in `__build_lxml` was to have the `pretty_print=true`. I wanted to have indented XML wherever possible.
So it seems the reasoning for the existing code is to be able to pretty-print the output, which `tostring` can do but `write_output` doesn't try to do.  When the doc build is producing a manpage the pretty printing doesn't apply, since the result is going to be troff anyway, not xml. So maybe it would work to have the `except` clause fall back to calling `write_output`?
So per the previous comment, how about:
```python
try:
    with open(str(target[0]), "wb") as of:
        of.write(etree.tostring(result, pretty_print=True))
except TypeError:
    result.write_output(str(target[0]))
```

Resolving patch:
diff --git a/src/engine/SCons/Tool/docbook/__init__.py b/src/engine/SCons/Tool/docbook/__init__.py
--- a/src/engine/SCons/Tool/docbook/__init__.py
+++ b/src/engine/SCons/Tool/docbook/__init__.py
@@ -351,11 +351,16 @@ def __build_lxml(target, source, env):
     else:
         result = transform(doc)
 
+    # we'd like the resulting output to be readably formatted,
+    # so try pretty-print. Sometimes (esp. if the output is
+    # not an xml file) we end up with a None type somewhere in
+    # the transformed tree and tostring throws TypeError,
+    # so provide a fallback.
     try:
         with open(str(target[0]), "wb") as of:
             of.write(etree.tostring(result, pretty_print=True))
-    except:
-        pass
+    except TypeError:
+        result.write_output(str(target[0]))
 
     return None
 


Test patch: