protected function resolveAllProperties(Properties $props)
    {
        foreach ($props->keys() as $name) {
            // There may be a nice regex/callback way to handle this
            // replacement, but at the moment it is pretty complex, and
            // would probably be a lot uglier to work into a preg_replace_callback()
            // system.  The biggest problem is the fact that a resolution may require
            // multiple passes.

            $value = $props->getProperty($name);
            $resolved = false;
            $resolveStack = [];

            while (!$resolved) {
                $fragments = [];
                $propertyRefs = [];

                PropertyHelper::getPropertyHelper($this->project)->parsePropertyString(
                    $value,
                    $fragments,
                    $propertyRefs
                );

                $resolved = true;
                if (count($propertyRefs) === 0) {
                    continue;
                }

                $sb = "";

                $j = $propertyRefs;

                foreach ($fragments as $fragment) {
                    if ($fragment !== null) {
                        $sb .= $fragment;
                        continue;
                    }

                    $propertyName = array_shift($j);
                    if (in_array($propertyName, $resolveStack)) {
                        // Should we maybe just log this as an error & move on?
                        // $this->log("Property ".$name." was circularly defined.", Project::MSG_ERR);
                        throw new BuildException("Property " . $propertyName . " was circularly defined.");
                    }

                    $fragment = $this->getProject()->getProperty($propertyName);
                    if ($fragment !== null) {
                        $sb .= $fragment;
                        continue;
                    }

                    if ($props->containsKey($propertyName)) {
                        $fragment = $props->getProperty($propertyName);
                        if (strpos($fragment, '${') !== false) {
                            $resolveStack[] = $propertyName;
                            $resolved = false; // parse again (could have been replaced w/ another var)
                        }
                    } else {
                        $fragment = "\${" . $propertyName . "}";
                    }

                    $sb .= $fragment;
                }

                $this->log("Resolved Property \"$value\" to \"$sb\"", Project::MSG_DEBUG);
                $value = $sb;
                $props->setProperty($name, $value);
            } // while (!$resolved)
        } // while (count($keys)
    }